Main analysis
In this main section, we present our findings of different facets pertaining to the Indego dataset. We subdivide our analysis into four interrelated sections: station usage, daily traffic, trip duration, and user profiles, in that particualr order. We will walk the reader through our thought process and findings, and finally present some concluding remarks.
Station usage
In this section, we examine the usage rate among the different Indego stations across Philadelphia. We motivate our analysis from a holistic perspective towards more granular analysis. This structural layout also holds true for subsequent sections. With that in mind, let us first patterns related to some of the more popular Indego stations. We subdivide our analysis to that of rental and return stations, which reflect popular starting and terminal destinations that people elect to choose in Philadelphia.
# Popular Stations - histogram
library(gridExtra)
popular_station <- df %>% group_by(year, month, start_station_id) %>% summarize(rental = n())
colnames(popular_station)[3] <- "id"
popular_station$id <- as.character(popular_station$id)
foo <- df %>% group_by(year, month, end_station_id) %>% summarize(return = n())
colnames(foo)[3] <- "id"
foo$id <- as.character(foo$id)
popular_station <- popular_station %>% full_join(foo, by = c("id" = "id", "year" = "year", "month" = "month"))
rm(foo)
popular_station <- tidyr::gather(popular_station, type, count, c(4,5))
colnames(popular_station)[3] <- "id"
popular_station[is.na(popular_station)] <- 0
popular_station <- popular_station[popular_station$id!="#N/A",]
station_loc$id <- as.character(station_loc$id)
reduced_station <- popular_station %>% left_join(station_loc, by=c("id"="id")) %>% na.omit
reduced_station$quarter <- (as.numeric(as.character(reduced_station$month)) - 1) %/% 3 + 1
reduced_station <- reduced_station %>% group_by(id, name, lat, long, type, year, quarter) %>% summarize(count = sum(count))
# Earliest quarter: 2015Q2, Latest quarter: 2016Q4
reduced_station$year <- as.numeric(reduced_station$year)
rs_first_rental <- reduced_station[reduced_station$year==2015&reduced_station$quarter==2&reduced_station$type=="rental",]
rs_last_rental <- reduced_station[reduced_station$year==2016&reduced_station$quarter==4&reduced_station$type=="rental",]
rs_first_return <- reduced_station[reduced_station$year==2015&reduced_station$quarter==2&reduced_station$type=="return",]
rs_last_return <- reduced_station[reduced_station$year==2016&reduced_station$quarter==4&reduced_station$type=="return",]
rs_first_rental <- rs_first_rental[order(-rs_first_rental$count),][1:10,]
rs_last_rental <- rs_last_rental[order(-rs_last_rental$count),][1:10,]
rs_first_return <- rs_first_return[order(-rs_first_return$count),][1:10,]
rs_last_return <- rs_last_return[order(-rs_last_return$count),][1:10,]
rs_first_rental$name <- factor(rs_first_rental$name, levels = rs_first_rental$name[order(rs_first_rental$count)])
rs_last_rental$name <- factor(rs_last_rental$name, levels = rs_last_rental$name[order(rs_last_rental$count)])
rs_first_return$name <- factor(rs_first_return$name, levels = rs_first_return$name[order(rs_first_return$count)])
rs_last_return$name <- factor(rs_last_return$name, levels = rs_last_return$name[order(rs_last_return$count)])
p1 <- ggplot(rs_first_rental, aes(x = name, y = count)) +
geom_bar(stat = 'identity', color="blue", fill="blue") +
geom_text(aes(label=count), hjust=-0.05, size=2.5) +
coord_flip() +
ggtitle("Most Popular Rental Station 2015Q2") +
xlab("Frequency") +
ylab("Station") +
scale_color_fivethirtyeight("cyl") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p2 <- ggplot(rs_last_rental, aes(x = name, y = count)) +
geom_bar(stat = 'identity', color="blue", fill="blue") +
geom_text(aes(label=count), hjust=-0.05, size=2.5) +
coord_flip() +
ggtitle("Most Popular Rental Station 2016Q4") +
xlab("Frequency") +
ylab("Station") +
scale_color_fivethirtyeight("cyl") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p3 <- ggplot(rs_first_return, aes(x = name, y = count)) +
geom_bar(stat = 'identity', color="blue", fill="blue") +
geom_text(aes(label=count), hjust=-0.05, size=2.5) +
coord_flip() +
ggtitle("Most Popular Return Station 2015Q2") +
xlab("Frequency") +
ylab("Station") +
scale_color_fivethirtyeight("cyl") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p4 <- ggplot(rs_last_return, aes(x = name, y = count)) +
geom_bar(stat = 'identity', color="blue", fill="blue") +
geom_text(aes(label=count), hjust=-0.05, size=2.5) +
coord_flip() +
ggtitle("Most Popular Return Station 2016Q4") +
xlab("Frequency") +
ylab("Station") +
scale_color_fivethirtyeight("cyl") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p1 <- ggplot_gtable(ggplot_build(p1))
p2 <- ggplot_gtable(ggplot_build(p2))
p3 <- ggplot_gtable(ggplot_build(p3))
p4 <- ggplot_gtable(ggplot_build(p4))
maxWidth = grid::unit.pmax(p1$widths[2:3], p2$widths[2:3], p3$widths[2:3], p4$widths[2:3])
p1$widths[2:3] <- maxWidth
p2$widths[2:3] <- maxWidth
p3$widths[2:3] <- maxWidth
p4$widths[2:3] <- maxWidth
# Plot histograms for selected quarters
grid.arrange(p1, p3, p2, p4, heights = c(3, 3))

In the above graph, we see that the most popular stations are somewhat invariant to time. We chose the first and latest quarters to determine most popular rental and return stations. In all cases, the most popular rental and return station was Rittenhouse Square. This is intuitive as it is not only a popular tourist destination, being hailed as “one of the finest urban public spaces in the United States,” (at least according to Wikipedia) but it is also in close proximity to the epicenter of Philadelphia. More specifically, in Q2 of 2015, the top five most popular rental and return stations were all the same, with the exact same ranking. Thus, it would seem that there is no preference for dropoff or pickup stations, but simply hubs for bike users. In Q4 of 2016, we observe many of the same return and and rental stations, but differences exist. For instance, Philadelphia Museum of Art (PMOA) is no longer in the top ten most popular return or rental stations whereas University City station is, which was not in Q2 of 2015. These variability are difficult to explain based on the dataset alone, but we surmise a couple of possible causes. First, the demographic of Indego users has shifted over time (from intrigued users to a those using it for routine trips). Second, seasonality may affect popular rental/return stations. There is a larger tourist cohort during the summer so that may be why PMOA is so high up in the rankings for Q2 of 2015. These are all conjectural, and it may serve as an interesting avenue for further exploration. However this is beyond the scope of our project.
We postulate that the popularity of certain stations across time is due to the fact they are in areas of heavy pedestrian traffic (e.g. city centers), and they are in close proximity to one another but outside comfortable walking range. We thus put this idea to the test by considering spatial patterns and distances amongst these more popular stations.
# Popular stations - maps
rs_first_return$type <- "Return"
rs_last_return$type <- "Return"
rs_first_rental$type <- "Rental"
rs_last_rental$type <- "Rental"
Q2Y15 <- rbind(rs_first_return, rs_first_rental)
Q4Y16 <- rbind(rs_last_return, rs_last_rental)
pal <- colorFactor(c("navy", "red"), domain = c("Return", "Rental"))
# Purple: popular for both return and rental
# Red: only for rental
# Blue: only for return
# Size: total frequency within the quarter
# for Q2 of 2015
leaflet(Q2Y15) %>%
addTiles() %>%
addProviderTiles('CartoDB.Positron') %>%
setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
addCircleMarkers(
radius = ~sqrt(count)/10,
color = ~pal(type),
stroke = FALSE, fillOpacity = 0.5,
popup = ~name
)
# for Q4 of 2016
leaflet(Q4Y16) %>%
addTiles() %>%
addProviderTiles('CartoDB.Positron') %>%
setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
addCircleMarkers(
radius = ~sqrt(count)/10,
color = ~pal(type),
stroke = FALSE, fillOpacity = 0.5,
popup = ~name
)
In the above two interactive maps, the color purple represents popular rental and return stations, red for rental, and blue for return. The size of the dot denotes the total frequency of rides within the quarter (top: Q2 of 2015, bottom: bottom: Q4 of 2016). Crudely in line with our hypothesis, most of the most popular stations (by dot size) are concentrated in the epicenter of Philadelphia, which is roughly between Rittenhouse Square and Chinatown. We also see that PMOA is off in the distance to the northeast, but was a popular rental and return station in Q2 of 2015. This also corresponds to our theory that summer made this a popular hub for bike users. In Q4 of 2016, the shift in clustering of popular stations revolves around the two shores of Schuylkill River. This may corroborate our observation that people use it as a means to cross the river for routine commutes (e.g. between University City/Powelton Village and Rittenhouse Square) that are out of walking range but close enough for a bike ride. This is purely speculative but makes for an interesting idea to further investigate. We will touch on this a bit more in the next section.
Daily traffic
We put the theory of using Indego as a form of commute to the test, using daily traffic as a proxy for measurements. We picked Q2 of 2016, which is the quarter with the second most bike rides. We ideally would have liked to chosen Q3 of 2016 (most recent summer quarter), which is the most popular quarter, but this time period overlaps with summer vacation for many students. Since we did not want to ignore this particular demographic when analyzing the use of bike trips as a form of commute, we thus settled on Q2 of 2016. We considered average hourly uses of Indego bikes per day for every day of Q2 of 2016. We then plotted this on a 24-hour time-scale, which is shown below. As illustrated by the plot, there are drastic differences between weekday and weekend usage across any standard 24-hour window. On weekdays, peak periods of bike trips were around 8:00 AM and 5:00 PM, which correspond to the time frames that most individuals commute respectively to and from work and school. On weekends, however, peak usage occured during the afternoon. Moreover, more bike trips were observed on weekdays for almost any given time interval than that of weekends. Thus, this may potentially demonstrate that Indego has become a means for everyday commute in Philadelphia.
# 2. Peak Periods (group by hour, weekdays/weekends, season)
# 2.1 Weekdays/weekends histogram of start (rental) hours for all stations in 2016Q2
library(lubridate)
Q2Y16 <- df[df$year==2016 & df$month %in% c("04","05","06"), ]
Q2Y16$day_type <- chron::is.weekend(Q2Y16$date)
Q2Y16$day_type <- ifelse(Q2Y16$day_type == 1, "Weekends", "Weekdays")
n_wknds <- length(unique(Q2Y16[Q2Y16$day_type=="Weekends",14]))
n_wkdys <- length(unique(Q2Y16[Q2Y16$day_type=="Weekdays",14]))
wknds_peak <- Q2Y16[Q2Y16$day_type=="Weekends",] %>% group_by(s_hour,day_type) %>% summarize(rental_freq = n()/n_wknds)
wkdys_peak <- Q2Y16[Q2Y16$day_type=="Weekdays",] %>% group_by(s_hour,day_type) %>% summarize(rental_freq = n()/n_wkdys)
peak <- rbind(wknds_peak, wkdys_peak)
rm(wknds_peak, wkdys_peak)
peak$s_hour <- as.numeric(as.character(peak$s_hour))
foo1 <- c(0,4,8,12,16,20)
foo2 <- c("12am","4am","8am","12pm","4pm","8pm")
ggplot(peak, aes(s_hour, rental_freq)) +
geom_line(aes(color=day_type)) +
geom_point(size = 1.1, aes(color=day_type)) +
labs(title="Peak Rental Hours 2016Q2",
subtitle="Average Daily Rental Frequency by Hour") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2)

We now extend our analysis to all quarterly data available, which is shown in the plot immediately below. Much of the same trends from Q2 of 2016 can be generalized to other quarters. Apart from a few data points, the number of bike trips on weekdays are almost always larger than that of weekends. However, we also observe the aforementioned seasonality of bike trips, in which more bike trips are observed in the summer than in the winter months. This is especially true for trips on weekend (e.g. usage on weekends in Sept of 2015 is by far the largest). Therefore, use of Indego as leisure during summer weekends may be the causal variable of the peaks we observe.
# 2.2 Month aggregation of results
quarter_aggr <- df
#quarter_aggr$quarter <- (as.numeric(as.character(quarter_aggr$month)) - 1) %/% 3 + 1
quarter_aggr$day_type <- chron::is.weekend(quarter_aggr$date)
quarter_aggr$day_type <- ifelse(quarter_aggr$day_type == 1, "Weekends", "Weekdays")
quarter_aggr <- quarter_aggr %>% select(year, month, date, day_type)
quarter_aggr <- quarter_aggr %>% group_by(date, year, month, day_type) %>% summarise(count=n())
quarter_aggr <- quarter_aggr %>% group_by(year, month, day_type) %>% summarise(average=mean(count))
quarter_aggr$no <- (0:(nrow(quarter_aggr)-1)) %/% 2 + 1
foo1 <- seq(1,21,by=3)
foo2 <- c("Apr 2015","Jul 2015","Oct 2015", "Jan 2016", "Apr 2016","Jul 2016","Oct 2016")
ggplot(quarter_aggr, aes(no, average)) +
geom_line(aes(color=day_type)) +
geom_point(size = 1.1, aes(color=day_type)) +
labs(title="Monthly Rental Pattern",
subtitle="Daily Average of Rental Frequencies") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2) +
theme(axis.text.x = element_text(angle = 30, hjust = 1))

It is important to note that one of the more glaring deficiencies of our raw dataset is the lack of historical data; we only have data from April 2015 onwards. It thus makes it more difficult to draw conclusive remakrs regarding yearly or even seasonal patterns that require data over longer time periods. Nonetheless, several important trends can still be observed over the time period for which the dataset is available.
For instance, let us take a look at monthly bike trips as determined from the number of average trips per day, shown below. Such an aggregation, as opposed to taking the monthly average as is, was done to smooth out daily fluctuations that can be considered noise as we make our time scale more granular. We observe two main themes. First, there seems to be particular months in which bike trips are more and less frequent. Across any given calendar year, there are more bike rides through the summer, ultimately peaking in September. Bike trips decline thereafter, hitting low points in December/January. This is intuitive as good weather is correlated with how willing and practical bike rides function as a means of transportation. Thus, with abysmal weather conditions in the winter, people are probably less willing to use Indego bikes. Second, we observe Indego’s popularity increase through time based on the number of bike trips in a given month as compared to the same month in a preceding year. In Sept 2016, usage was at an all-time high, with more than 2600 rides per day (cf. approximately 2250 rides in Sept 2016). In Dec 2016, bike rides were roughly a little higher than 1000 rides per day (cf. 600 in Dec 2015). It should be noted that confounding factors such as weather conditions in a given year (e.g. El Nino) and the insufficient historical data we have on-hand may somewhat render our results questionable. We, however, believe this is not the case since more ecofriendly means of transportation should theoretically become more popular due to growing environmental sentiments in the mass population.
# Monthly Bike Trips
df_count_by_month <- df %>% group_by(year, month) %>% summarize(count = n())
df_average_by_month <- df %>% group_by(year, month, day) %>% summarize(count = n())
df_average_by_month <- df_average_by_month %>% group_by(year, month) %>% summarize(avg = mean(count))
df_count_by_month$date <- zoo::as.yearmon(paste(df_count_by_month$year, df_count_by_month$month), "%Y %m")
df_count_by_month$average <- df_average_by_month$avg
df_count_by_month$no <- 1:nrow(df_count_by_month)
df_count_by_month <- df_count_by_month[,-c(1,2)]
rm(df_average_by_month)
df_count_by_month <- tidyr::gather(df_count_by_month, type, value, c(1,3))
foo1 <- c(0, 500, 1000, 1500, 2000, 2500, 3000)
foo2 <- c(3, 6, 9, 12, 15, 18, 21)
ggplot() +
geom_line(aes(no, value, color="blue"),
df_count_by_month[df_count_by_month$type=="average",]) +
geom_point(size = 1.1) +
ggtitle("Monthly Indego Bike Trips\n(Average per day)") +
xlab("Month") +
ylab("Count") +
scale_color_fivethirtyeight("cyl") +
theme_fivethirtyeight() +
theme(axis.text.x = element_text(angle = 30, hjust = 1),legend.position="none") +
scale_y_continuous(limits = c(0, 3000), breaks = foo1) +
scale_x_continuous(breaks = foo2, labels = df_count_by_month$date[foo2]) +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))

As an interesting tangent, we examine the most popular routes based on a daily average of trips betweeen all stations across available quarters. We see that the most popular routes frequented by most users are those between hubs (i.e. already popular stations). In the interactive map below, we plot the Euclidean distance of the top five most popular routes. For instance, one such route is that between University Station and 23rd and South (i.e. downtown). It is worth noting that most of these routes are either across the downtown area or across the Schuylkill River, which somewhat substantiates the assertion that popular routes are a means for commute.
######## Popular Routes ########
# Content
# Most Popular 20 Routes
# Single dots means rent from these stations and return to these stations as well
popular_route <- df %>% group_by(date, year, month, start_station_id, end_station_id) %>% summarise(count=n())
popular_route <- popular_route %>% group_by(start_station_id, end_station_id) %>% summarise(avg=mean(count)) %>% na.omit
popular_route <- popular_route[order(-popular_route$avg),][1:20,]
# Extract all points
loc <- c(popular_route$start_station_id, popular_route$end_station_id) %>% unique %>% as.integer
station_loc$id <- as.numeric(station_loc$id)
dot_loc <- data.frame(id=loc) %>% left_join(station_loc, by=c("id"="id")) %>% na.omit
l1 <- leaflet(dot_loc) %>%
addTiles() %>%
addProviderTiles('CartoDB.Positron') %>%
setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
addCircleMarkers(
color = "navy",
stroke = FALSE, fillOpacity = 0.5,
popup = ~name
)
popu <- popular_route[popular_route$start_station_id!=popular_route$end_station_id,]
dot_loc2 <- dot_loc
colnames(popu)[1] <- "id"
popu$id <- as.numeric(popu$id)
popu <- popu %>% left_join(dot_loc2, by=c("id"="id")) %>% na.omit
colnames(popu) <- c("sid", "id", "avg", "s_lat", "s_long", "name", "date")
dot_loc2$id <- as.numeric(dot_loc2$id)
popu <- popu %>% left_join(dot_loc2, by=c("id"="id")) %>% na.omit
colnames(popu) <- c("sid", "id", "avg", "s_lat", "s_long", "s_name", "date", "e_lat", "e_long", "e_name", "date_e")
for(i in 1:nrow(popu)){
l1 <- addPolylines(l1, lat = as.numeric(popu[i, c(4, 8)]), lng = as.numeric(popu[i, c(5, 9)]))
}
l1
Based on the above findings, we can say with a certain level of confidence that the volume of bike trips is an important indicator of Indego’s utility rate over different cross-sections of time. However, it does little to tell us how (not how much) Indego is being utilized. With this in mind, we motivate our next portion of analysis.
Since the inherent purpose of bike share networks are to mediate relatively short commutes, we want to examine the length of each trip. We surmise that shorter trips (0-30 minutes) should be the ideal range since anything beyond that is simply exercise and other transportation methods become increasingly more appealing. We consequently explore features related to the length of bike rides next.
Trip durations
In this section, we try to elucidate the more salient patterns pertaining to trip durations. From the two histograms below, which illustrate the durations of all trips in Q2 and Q4 of 2016 across weekdays and weekends, we observe that most trips taken were under 15 minutes. These two particular quarters were chosen since they are the latest quarters that also offers a contrast in seasonality. During weekdays for both quarters, shorter commutes (< 15 minutes) were the overwhelming majority of trip durations. They make up more than 70% of the total count. The same is true for weekends, but to a lesser extent. This may once again be an indicator that bikes used during weekdays are for commute (e.g. to and from work and school) so they are inherently shorter, whereas bikes are used for more recreational purposes on weekends so their time of use is naturally longer. The latter is corroborated by the fact that during summer weekends, the frequency of longer trips outweighed that of shorter trips.
If we ignore any differentiation of trip durations between weekdays and weekends, we observe much of the same trends. For Q4 of 2016, the latest quarterly data available to us, the most prevalent duration of bike rides lie between 5 and 10 minutes. In fact, the overwhelming majority of trips are less than 30 minutes. On the contrary, we see that long trips are actually outliers in our dataset. Trips between two and four hours only make up 0.9% of all rides in Q4 of 2016, and trips exceeding 4 hours make up 0.7% of the total.
# 1. General Pattern Visualization
# 1.1 Trip Duration Distributions (dimension of time)
# Weekdays/weekends in 2016Q2 and 2016Q4
library(ggthemes)
Q2Y16 <- df[df$year==2016 & df$month %in% c("04","05","06"), ]
Q2Y16$day_type <- chron::is.weekend(Q2Y16$date)
Q2Y16$day_type <- ifelse(Q2Y16$day_type == 1, "Weekends", "Weekdays")
Q4Y16 <- df[df$year==2016 & df$month %in% c("10","11","12"), ]
Q4Y16$day_type <- chron::is.weekend(Q4Y16$date)
Q4Y16$day_type <- ifelse(Q4Y16$day_type == 1, "Weekends", "Weekdays")
Q2Y16$duration_cut <- cut(as.numeric(Q2Y16$duration), c(0,5,10,15,20,30,60,120,240,1440))
Q4Y16$duration_cut <- cut(as.numeric(Q4Y16$duration), c(0,5,10,15,20,30,60,120,240,1440))
Q2Y16_dur <- Q2Y16 %>% group_by(day_type, duration_cut, date) %>% summarise(count=n())
Q2Y16_dur<- Q2Y16_dur %>% group_by(day_type, duration_cut) %>% summarise(average=mean(count))
Q2Y16_dur$Quarter <- "2016Q2"
Q4Y16_dur <- Q4Y16 %>% group_by(day_type, duration_cut, date) %>% summarise(count=n())
Q4Y16_dur<- Q4Y16_dur %>% group_by(day_type, duration_cut) %>% summarise(average=mean(count))
Q4Y16_dur$Quarter <- "2016Q4"
dur_cut <- rbind(Q2Y16_dur, Q4Y16_dur)
rm(Q2Y16_dur, Q4Y16_dur, Q2Y16, Q4Y16)
foo <- c("<5min", "5-10min", "10-15min", "15-20min", "20-30min", "30-60min", "1-2hr", "2-4hr", ">4hr")
ggplot(dur_cut, aes(duration_cut, average)) +
geom_bar(aes(fill=Quarter), stat = 'identity', position = position_dodge()) +
facet_grid(. ~ day_type, scales = "free") +
labs(title="Daily Trip Duration Histogram",
subtitle="For 2016Q2 and 2016Q4",
xlab="") +
scale_color_fivethirtyeight(" ") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_discrete(labels = foo) +
theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
scale_fill_manual(values = c("#0072B2", "#CC79A7"))

We next examine the median rental duration for each station, particularly for Q2 to Q4 of 2016. We chose this range since it was the only range of contiuous quarterly data that had complete station entries for trip durations. In the interactive map below, larger circles correspond to larger values with respect to trip durations. The color scheme remains the same as previously mentioned. Therefore, a larger red circle implies that a bike user rented from a particular station and rode their bike for a longer duration. We see that larger red circles correspond to stations in the outskirts of Philadelphia. Therefore, they either used the bike for a longer commute or they used for leisurely purposes. For instance, one of the larger red circles denotes PMOA, which we previously mentioned as a popular station during the summer and weekends. However, it is impossible to differentiate between the two based on our dataset alone. It is however intriguing in nature.
# 1.2 Median Rental Duration for Each Station (2016 Q2-Q4) (dimension of space)
# choose 2016 Q2-Q4 since all stations have complete data in this period
station_ana2 <- df[df$year==2016 & df$month %in% c("04","05","06","07","08","09","10","11","12"), ]
median_stat <- station_ana2 %>% group_by(start_station_id) %>% summarise(med=median(duration))
# Plot in a map, dot size = median rental time
colnames(median_stat)[1] <- "id"
station_loc$id <- as.numeric(station_loc$id)
median_stat$id <- as.numeric(median_stat$id)
median_stat <- median_stat %>% left_join(station_loc, by=c("id"="id")) %>% na.omit
content <- paste("<b>", as.character(median_stat$name), "</b><br>",
"Median Rental Time: ", median_stat$med, "minutes", "<br>")
median_stat$chosen <- median_stat$id %in% c("3020", "3050", "3124")
median_stat$chosen <- ifelse(median_stat$chosen == 1, "Chosen", "Unchosen")
pal <- colorFactor(c("navy", "red"), domain = c("Chosen", "Unchosen"))
leaflet(median_stat) %>%
addTiles() %>%
addProviderTiles('CartoDB.Positron') %>%
setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
addCircleMarkers(
radius = ~med/2,
color = ~pal(chosen),
stroke = FALSE, fillOpacity = 0.5,
popup = ~content
)
The dots colored in navy are those that we chose to further analyze, which we present here. They correspond to the following stations:
- 3020: University City Station
- 3050: 9th & Arch (downtown)
- 3124: Race Street Pier (away from university and downtown)
# 2. Station-level rental time
# Select stations
# Three locations that could have totally different travel patterns:
# 3020: University City Station, global avg duration 17.8
# 3050: 9th & Arch, (downtown), global avg duration 22.4
# 3124: Race Street Pier, (away from university and downtown), global avg duration 43.4
# 2.1 Over hour of a day
station_ana <- df[df$year==2016 & df$month %in% c("04","05","06","07","08","09","10","11","12"), ]
foo <- station_ana %>% group_by(start_station_id) %>% summarise(average=mean(duration)) # select stattions
station_ana <- station_ana[station_ana$duration<240,] # !! Truncate duration at 4 hrs to remove outliers
station_ana <- station_ana[station_ana$start_station_id %in% c("3020", "3050", "3124"),]
station_ana$day_type <- chron::is.weekend(station_ana$date)
station_ana$day_type <- ifelse(station_ana$day_type == 1, "Weekends", "Weekdays")
foo <- station_ana %>% group_by(start_station_id) %>% summarise(count=n()) # check total trips
station_ana <- station_ana %>% group_by(start_station_id, day_type, s_hour) %>% summarise(avg_dur=mean(duration))
foo <- data.frame(start_station_id = rep(c("3020", "3050", "3124"),each=48),
day_type = rep(rep(c("Weekdays", "Weekends"),each=24),3),
s_hour = rep(0:23,6))
station_ana$s_hour <- as.numeric(as.character(station_ana$s_hour))
station_ana$start_station_id <- as.character(station_ana$start_station_id)
station_ana$day_type <- as.character(station_ana$day_type)
station_ana <- station_ana %>% right_join(foo, by=c("start_station_id"="start_station_id",
"day_type"="day_type",
"s_hour"="s_hour"))
station_ana[is.na(station_ana)] <- 0
foo1 <- c(0,4,8,12,16,20)
foo2 <- c("12am","4am","8am","12pm","4pm","8pm")
ggplot(station_ana[station_ana$start_station_id=="3020",], aes(s_hour, avg_dur)) +
geom_line(aes(color=day_type)) +
geom_point(size = 1.1, aes(color=day_type)) +
labs(title="Average Trip Duration 2016 Q2-Q4",
subtitle="At (3020) University City Station, Total Trips: 11920") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2)

ggplot(station_ana[station_ana$start_station_id=="3050",], aes(s_hour, avg_dur)) +
geom_line(aes(color=day_type)) +
geom_point(size = 1.1, aes(color=day_type)) +
labs(title="Average Trip Duration 2016 Q2-Q4",
subtitle="At (3050) 9th & Arch, Total Trips: 6121") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2)

ggplot(station_ana[station_ana$start_station_id=="3124",], aes(s_hour, avg_dur)) +
geom_line(aes(color=day_type)) +
geom_point(size = 1.1, aes(color=day_type)) +
labs(title="Average Trip Duration 2016 Q2-Q4",
subtitle="At (3124) Race Street Pier, Total Trips: 3729") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2)

In the first plot (University City Station), we consdier the station close to a university. We see that on average, longer trips occur on the weekend. In fact, none of the trips on weekdays exceed 25 minutes. We also observe that in both cases, longer trips tend to occur during the day, whereas shorter trips occur in the early morning and towards night time. In the second plot delineating the downtown station (9th & Arch), we see much of the same trend as that of the previous case. However, the discrepancy between trip durations across a 24-hour window on the weekends and the weekdays is much more pronounced. In our last plot above (Race Street Pier), we consider a station far from a university or downtown, which we can consider to be “more rural.” Here, trips on weekdays and weekends mirror one another, showing they are closely related. Moreover, trips from this more rural station are all much longer, most exceeding 20 minutes irrespective of time intervals during the week. Therefore, it would seem that different stations based on their geographical location serve a different purpose for bike riders. Stations in more densely populated areas tend to favor shorter rides, whereas longer rides are more prevalent in more remote locations. It is important to note that the magnitude of number of trips may introduce some sort of counfounder on our insights (e.g. data imbalance), but we ignore this here, since it is difficult to address this issue given the limited historical data we have. This is a problem that would be solved naturally as more data becomes available.
# 2.2 Over month of a year
station_ana2 <- df[df$year==2016 & df$month %in% c("04","05","06","07","08","09","10","11","12"), ]
station_ana2 <- station_ana2[station_ana2$start_station_id %in% c("3020", "3050", "3124"),]
station_ana2 <- station_ana2[station_ana2$duration<240,] # !! Truncate duration at 4 hrs to remove outliers
station_ana2 <- station_ana2 %>% group_by(start_station_id, month) %>% summarise(med=median(duration))
colnames(station_ana2)[1] <- "id"
station_ana2$id <- as.numeric(station_ana2$id)
station_ana2 <- station_ana2 %>% left_join(station_loc, by=c("id"="id"))
station_ana2$month <- as.numeric(as.character(station_ana2$month))
foo1 <- 4:12
foo2 <- c("Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec")
ggplot(station_ana2, aes(month, med)) +
geom_line(aes(color=name)) +
geom_point(size = 1.1, aes(color=name)) +
labs(title="Median of Trip Duration",
subtitle="Over Months (2016 Q2-Q4)") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2)

We realize it may be difficult to examine the trends of all three plots concurrently so we thus plot the median trip duration of all three stations in one single plot, which is shown above. It is interesting to note that over time, shorter rides become the prevalent theme. This is true for all three stations. However, the more rural station is still characterized by longer trips relative to the other two stations that are close to a university and city center.
Thus far, we have examined how and how much Indigo is utilized. However, we still do not know how users select to utilize Indego. For instance, which methods of payment are more common? Is there a change in preference over time? We seek to answer these questions in the next and final section of our analysis.
User profiles
In this section, we examine the attributes related to Indego bike users. Since the dataset provides only two dimensions regarding this aspect, we use the conventional mosaic plot to examine the proportions of different combinations of Indego users. These two features are passholder types (Indego30, IndegoFlex, and walkup) and type of trips (one-way, roundtrip).
Here is some more relevant information regarding the different passholder types:
- Indego30: $15/month, unlimited one hour trips
- IndegoFlex: $10/year base fee, and $4/hour
- Walk-up: $8/hour
# 1.1 Mosaic plot of overall data
all_data_mosaic <- df %>% group_by(passholder_type, trip_route_category) %>% summarize(count = n())
colnames(all_data_mosaic) <- c("Pass", "Trip", "count")
library(vcd)
vcd::mosaic(xtabs(count ~ Trip + Pass, data = all_data_mosaic),
direction = c("v", "h"),
gp = gpar(fill = c("light green", "light blue", "light yellow")),
spacing = spacing_highlighting,
labeling = labeling_border(rot_labels = c(0,90,0,0)))

For all available quarters, we can see that the majority of users drastically favor one-way over round trips, and Indego30 passes over walkup payment plans. IndegoFlex is the least popular option regardless of route type. In fact, the combination of one-way trips with Indego30 is overwhelming characteristic, accounting for >50% of all users, followed by one-way with walk-ups. However, this is a extremely rough representation of user profiles since we ignore how user profiles vary over time. We examine this next.
Let us examine the most popular option (Indego30 x one-way), as shown below. We see that this option has become more popular at a steady rate, in which more than 75% of users choose this option in the lastest quarter.
# 1.2 Type of passholders, frequency of round-trips (for month)
# for most popular type of trip route and pass type
# 2. Tendency of Moving to Most Popular Category
# Plot of the percentage of (passholder_type, trip_route_category) over quarters
# For most popular category
perc_category <- df %>% group_by(passholder_type, trip_route_category, year, month) %>% summarize(count = n())
perc_category$quarter <- (as.numeric(as.character(perc_category$month)) - 1) %/% 3 + 1
perc_category <- perc_category %>% group_by(year, quarter, passholder_type, trip_route_category) %>% summarise(count_qt=sum(count))
perc_category$type <- paste(perc_category$trip_route_category, "x", perc_category$passholder_type)
perc_category <- perc_category %>% group_by(year, quarter) %>% mutate(total=sum(count_qt))
perc_category$perc <- round(100*(perc_category$count_qt / perc_category$total),2)
perc_category <- perc_category[,-c(3:5,7)]
perc_category$no <- rep(1:7,each=6)
foo1 <- 1:7
foo2 <- c("2015Q2","2015Q3","2015Q4", "2016Q1", "2016Q2","2016Q3","2016Q4")
foo3 <- c("One Way x Indego30")
tmp1 <- perc_category[perc_category$type %in% foo3,]
ggplot(tmp1, aes(no, perc)) +
geom_line(aes(colour=type)) +
geom_point(aes(colour=type),size = 1.1) +
labs(title="Percentage Change Over Quarters",
subtitle="For Most Popular Category\nOne Way x Indego30") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2) +
scale_y_continuous(limits = c(0, 100))

We see from the previous mosaic plot that round trips are not popular on average. We want to test whether there does indeed exist a decline in popularity over time. We see from the percentage change over quarters shown below that regardless of pass types, round trips make up an increasingly smaller fraction of the user demographic. We also postulate that the already unpopular combination of round trips and Indego Flex would make up an increasingly smaller proportion as well since they are more frequent travellers that would gradually move towards cheaper plans. This however is speculative and is contingent on a robust, large enough longitudinal dataset, which we do not have. Nontheless, this problem, albeit interesting, is beyond the scope of our analysis.
# For roundtrips
foo4 <- c("Round Trip x IndegoFlex", "Round Trip x Indego30", "Round Trip x Walk-up")
tmp2 <- perc_category[perc_category$type %in% foo4,]
ggplot(tmp2, aes(no, perc)) +
geom_line(aes(colour=type)) +
geom_point(aes(colour=type),size = 1.1) +
labs(title="Percentage Change Over Quarters",
subtitle="Over Categories") +
scale_color_fivethirtyeight("") +
theme_fivethirtyeight() +
theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
scale_x_continuous(breaks = foo1, labels = foo2) +
scale_y_continuous(limits = c(0, 7.15))

Based on the above analysis on user profiles, we see that as bike sharing becomes more popular over time in Philadelphia, more users opt for more cost-efficient payment plans, which is by far Indego30. The rise of one-way trips also conjecturally demonstrate that users are using Indego more frequently as a means of transportation over short distances or time periods. Obviously, we ignore many important factors related to user profiles. For instance, is there a preference for those renting from particular stations or during particular months? This is a natural extension of our analysis that we would like to further explore time permitting. However, we believe that our current set of analysis on user profiles presented here serves to provide an overarching representation of user profiles.
LS0tCnRpdGxlOiAiU1RBVCA1NzAyOiBGaW5hbCBQcm9qZWN0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCiMjIyMgTW90aXZhdGlvbgogCkluZGVnbyBpcyB0aGUgZGUgZmFjdG8gYmlrZSBzaGFyaW5nIG5ldHdvcmsgaW4gUGhpbGFkZWxwaGlhLiBJdCBoYXMgYmVjb21lIGEgcG9wdWxhciwgbW9yZSBlY28tZnJpZW5kbHkgYWx0ZXJuYXRpdmUgZm9yIHNob3J0IHRyaXBzIGFjcm9zcyB0aGUgY2l0eSBzaW5jZSBsYXVuY2hpbmcgaW4gbGF0ZSBBcHJpbCBvZiAyMDE1LiBIb3dldmVyLCBsaXR0bGUgaGFzIGJlZW4gZG9uZSB0byBhbmFseXplIHRoZSBwdWJsaWNseSBhdmFpbGFibGUgZGF0YSBhc3NvY2lhdGVkIHdpdGggSW5kZWdvLiBUaHVzLCB3ZSB0aGluayBpdCB3b3VsZCBiZSBiZW5lZmljaWFsIHRvIHRoZSBwdWJsaWMgaWYgd2UgcXVhbnRpZnkgYW5kIGFzc2VzcyBzZXZlcmFsIHBlcnRpbmVudCBmZWF0dXJlcyBhbmQgcGF0dGVybnMgcmVsYXRlZCB0byBJbmRlZ28sIGdlbmVyYXRpbmcgaW5zaWdodHMgb24gaG93IHRoZSBiaWtlIHNoYXJpbmcgbmV0d29yayBpcyB1dGlsaXplZCBhY3Jvc3MgdGhlIGNpdHkuIE1vcmUgc3BlY2lmaWNhbGx5LCB3ZSB3YW50IHRvIG1vcmUgY2xvc2VseSBzdHVkeSBhbmQgYW5zd2VyIHF1ZXN0aW9ucyByZWxhdGVkIHRvIHRoZSBmb2xsb3dpbmcgZm91ciB0aGVtZXM6CgoqIFN0YXRpb24gdXNhZ2UgKHVzYWdlIHJhdGUsIHBlYWsgcGVyaW9kcywgcG9wdWxhciByZW50YWwvcmV0dXJuIHN0YXRpb25zLCBldGMuKSAKKiBEYWlseSB0cmFmZmljIChwb3B1bGFyIHJvdXRlcywgdm9sdW1lIGFjcm9zcyBkaWZmZXJlbnQgc2VjdGlvbnMsIHRyYXZlbCBwYXR0ZXJucywgZXRjLikKKiBUcmlwIGR1cmF0aW9ucyAobGVuZ3RoIG9mIHRyaXBzLCBtZWRpYW4gcmVudGFsL3JldHVybiB0aW1lLCBldGMuKQoqIFVzZXIgcHJvZmlsZXMgKHR5cGUgb2YgcGFzc2hvbGRlcnMsIGZyZXF1ZW5jeSBvZiByb3VuZC10cmlwcywgc2Vhc29uYWxpdHksIGV0Yy4pCgpFYWNoIG9mIHRoZSBhYm92ZSBmb3VyIGZlYXR1cmVzIHdpbGwgYmUgZXhwbGFpbmVkIGluIGZpbmVyIGRldGFpbHMgaW4gc3Vic2VxdWVudCBzZWN0aW9ucy4gVG8gcmVhbGl6ZSB0aGlzIG9iamVjdGl2ZSwgd2UgaGF2ZSBjcmVhdGVkIHN0YXRpYyBwbG90cyAoc2hvd24gaW4gdGhpcyBub3RlYm9vaykgdG8gZGVzY3JpYmUgc2FsaWVudCB0aGVtZXMgYXMgd2VsbCBhcyBhIFNoaW55IGFwcCAoW0dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL21hc29uc3VuL2dyNTcwMi1wcm9qKSksIHdoaWNoIGFsc28gY29udGFpbnMgcmVhbC10aW1lIHN0YXR1cyB1cGRhdGVzIG9mIGFsbCBJbmRlZ28gc3RhdGlvbnMgYWNyb3NzIFBoaWxhZGVscGhpYS4gV2Ugd2lsbCBhbHNvIHByb3ZpZGUgaW5zdHJ1Y3Rpb25zIG9uIHJ1bm5uaW5nIG91ciBhcHAgZnVydGhlciBkb3duIGluIG91ciByZXBvcnQuCgpPdXIgb2JqZWN0aXZlIHN0YXJ0aW5nIG91dCB3YXMgdG8gZXhhbWluZSBiaWtlIHNoYXJlIHByb2dyYW1zIGluIHRoZSBtb3N0IGRlbnNlbHkgcG9wdWxhdGVkIGNpdGllcyBhY3Jvc3MgdGhlIGVhc3Rlcm4gVW5pdGVkIFN0YXRlcywgbWFpbmx5IHZpYSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uIHRlY2hpbnF1ZXMuIFdlIGluaXRpYWxseSBjb25zaWRlcmVkIE5ZQydzIENpdGliaWtlIGFzIGEgcHJvbWlzaW5nIGNhbmRpZGF0ZSwgYnV0IGl0IGhhcyBhbHJlYWR5IGJlZW4gZXh0ZW5zaXZlbHkgYW5hbHl6ZWQgKHNlZSBbVG9kZCBTY2huZWlkZXIncyBibG9ncG9zdF0oaHR0cDovL3RvZGR3c2NobmVpZGVyLmNvbS9wb3N0cy9hLXRhbGUtb2YtdHdlbnR5LXR3by1taWxsaW9uLWNpdGktYmlrZXMtYW5hbHl6aW5nLXRoZS1ueWMtYmlrZS1zaGFyZS1zeXN0ZW0vKSBmb3IgYW4gZXhjZWxsZW50IGV4YW1wbGUpLiBUaGUgbWFyZ2luYWwgcmV0dXJucyBvZiBhbmFseXppbmcgc3VjaCBhIGRhdGFzZXQgYXJlIHRodXMgbWluaW1hbCwgaWYgbm90LCBuaWwuIEFzIGEgcmVzdWx0LCB3ZSBkZWNpZGVkIG9uIFBoaWxhZGVscGhpYSwgcHJpbWFybGl5IGZvciB0d28gcmVhc29uczogaXQgaXMgdGhlIHNlY29uZCBsYXJnZXN0IGNpdHkgc2F0aXNmeWluZyBvdXIgcHJlZGVmaW5lZCBjcml0ZXJpYSAoYWZ0ZXIgTllDKSwgYW5kIGFsc28gaGFzIGEgcmVsYXRpdmVseSBuZXcgYmlrZSBzaGFyZSBwcm9ncmFtLiBUaHVzLCB3ZSB0aG91Z2h0IHRoZSBJbmRlZ28gZGF0YXNldCB3b3VsZCBiZSBhbiBpZGVhbCBjaG9pY2UuIEl0IHdvdWxkIGFsc28gYmUgaW50ZXJlc3RpbmcgdG8gdXNlIG91ciBkYXRhc2V0IHRvIGNvbXBhcmUgYW5kIGNvbnRyYXN0IE5ldyBZb3JrIGFuZCBQaGlsYWRlbHBoaWEncyBiaWtlIHNoYXJlIHByb2dyYW1zLgoKIyMjIyBEYXRhIFNvdXJjZQoKVGhlIGRhdGFzZXQgaXMgcHVibGljbHkgYXZhaWxhYmxlIG9uIFtJbmRlZ28ncyB3ZWJzaXRlXShodHRwczovL3d3dy5yaWRlaW5kZWdvLmNvbS9hYm91dC9kYXRhLykuIEl0IGNhbiBiZSBmb3VuZCBieSBjbGlja2luZyB0aGUgPGk+QWJvdXQ8L2k+IHBhbmVsIGFuZCB0aGVuIHNlbGVjdGluZyA8aT5EYXRhPC9pPi4gQWxsIHF1YXJ0ZXJseSBkYXRhIGFyZSBsaXN0ZWQgdW5kZXIgdGhlIHNlY3Rpb24gPGk+VGhlIERhdGE8L2k+LCBpbiB3aGljaCBlYWNoIHN0YXRpb24ncyBpbmZvcm1hdGlvbiBpcyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgPGk+U3RhdGlvbiBUYWJsZTwvaT4gaHlwZXJsaW5rLiBJbiBhZGRpdGlvbiwgcmVhbC10aW1lIGRhdGEgdXNlZCBpbiBvdXIgU2hpbnkgYXBwIGlzIGF2YWlsYWJsZSB1bmRlciB0aGUgc2VjdGlvbiA8aT5JbmRlZ28gU3RhdGlvbiBTdGF0dXM8L2k+IHZpYSB0aGUgR2VvSlNPTiBoeXBlcmxpbmsuCgojIyBUZWFtCgoxLiBLZWppYSBTaGkgKGtzMzQwMyk6IGRhdGEgcHJvY2Vzc2luZywgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcywgdmlzdWFsaXphdGlvbiAoc3RhdGljIGFuZCBTaGlueSBwbG90cyksIHdyaXRpbmcvZWRpdGluZyByZXBvcnQuCjIuIE1hc29uIFN1biAoenMyMzIxKTogZGF0YSBwcm9jZXNzaW5nLCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLCBkZXZlbG9waW5nIGZyb250L2JhY2tlbmQgb2YgU2hpbnkgYXBwLCB3cml0aW5nL2VkaXRpbmcgcmVwb3J0LgozLiBIdW5nLVNoaSBMaW4gKGhsMjk5Nyk6IHByb29mcmVhZC4KCiMjIEFuYWx5c2lzIG9mIERhdGEgUXVhbGl0eQoKIyMjIyBRdWFydGVybHkgZGF0YQpGb3IgcXVhcnRlcmx5IGRhdGEsIHdlIGhhZCBzZXZlbiBmaWxlcyBpbiB0b3RhbCwgZWFjaCByZXByZXNlbnRpbmcgZGF0YSBiZXR3ZWVuIFEyIG9mIDIwMTUgYW5kIFE0IG9mIDIwMTYsIGluY2x1c2l2ZWx5LiBFYWNoIGNvbnRhaW5lZCB0aGUgc2FtZSBjb2x1bW5zOgoKKiB0cmlwX2lkICh1bmlxdWUgaWRlbnRpZmllciBvZiBhIGJpa2UgdHJpcCkKKiBkdXJhdGlvbiAobGVuZ3RoIG9mIHRyaXAgaW4gc2Vjb25kcyBvciBtaW51dGVzIGNhbGN1bGF0ZWQgYnkgY2hlY2staW4gYW5kIGNoZWNrLW91dCB0aW1lKQoqIHN0YXJ0X3RpbWUgKGNsb2NrIHN0YXJ0IHRpbWUgYW5kIGNhbGVuZGFyIGRhdGUpCiogZW5kX3RpbWUgKGNsb2NrIGVuZCB0aW1lIGFuZCBjYWxlbmFyIGRhdGUpCiogc3RhcnRfc3RhdGlvbl9pZCAodW5pcXVlIGlkZW50aWZpZXIgb2Ygc3RhcnRpbmcgc3RhdGlvbikKKiBzdGFydF9sYXQgKGxhdGl0dWRlIG9mIHN0YXJ0aW5nIHN0YXRpb24pCiogc3RhcnRfbG9uIChsb25naXR1ZGUgb2Ygc3RhcnRpbmcgc3RhdGlvbikKKiBlbmRfc3RhdGlvbl9pZCAodW5pcXVlIGlkZW50aWZpZXIgb2YgZW5kIHN0YXRpb24pCiogZW5kX2xhdCAobGF0aXR1ZGUgb2YgZW5kIHN0YXRpb24pCiogZW5kX2xvbiAobG9uZ2l0dWRlIG9mIGVuZCBzdGF0aW9uKQoqIGJpa2VfaWQgKHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBiaWtlIHVzZWQgZHVyaW5nIHRoZSB0cmlwKQoqIHBsYW5fZHVyYXRpb24gKHR5cGUgb2YgcGFzcyBieSBudW1iZXIgb2YgZGF5cywgZS5nLiB0aGlydHktZGF5IHBhc3MgaXMgc2hvd24gYXMgMzApCiogdHJpcF9yb3V0ZV9jYXRlZ29yeSAocm91bmQgdHJpcCBvciBvbmUtd2F5KQoqIHBhc3Nob2xkZXJfdHlwZSAod2Fsa3VwLCBJbmRlZ29GbGV4LCBvciBJbmRlZ28zMCkKCldlIGNvbWJpbmVkIHRoZXNlIGludG8gb25lIGRhdGEgZnJhbWUgKGNhbGxlZCA8aT5kZjwvaT4pIHVzaW5nIHRoZSBjb2RlIGFzIGZvbGxvd3MuIFRoZSBkZXNjcmlwdGlvbiBvZiBvdXIgcHJlcHJvY2Vzc2luZyBtZXRob2RzIGFyZSBlbWJlZGRlZCBhcyBjb21tZW50cyBpbiB0aGUgY29kZSBjaHVuayBiZWxvdy4gUGxlYXNlIHNlZSB0aGVtIGZvciBtb3JlIGRldGFpbGVkIGV4cGxhbmF0aW9ucy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyMjIHJlYWQgaW4gcXVhcnRlcmx5IGRhdGEgIyMjClkxNlE0IDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTQuY3N2IiwgaGVhZGVyPVQpClkxNlEzIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTMuY3N2IiwgaGVhZGVyPVQpClkxNlEyIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTIuY3N2IiwgaGVhZGVyPVQpClkxNlExIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTEuY3N2IiwgaGVhZGVyPVQpClkxNVE0IDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE1UTQuY3N2IiwgaGVhZGVyPVQpClkxNVEzIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE1UTMuY3N2IiwgaGVhZGVyPVQpClkxNVEyIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE1UTIuY3N2IiwgaGVhZGVyPVQpCmluZGVnb19zdGF0aW9ucyA8LSByZWFkLmNzdigiZGF0YS9pbmRlZ29fc3RhdGlvbnMuY3N2IiwgaGVhZGVyPVQpCiMjIyBwcmVwcm9jZXNzaW5nICMjIwojIHNlbGVjdCBjdXJyZW50bHkgYWN0aXZlIHN0YXRpb25zIGZyb20gbW9zdCByZWNlbnQgcXVhcnRlcmx5IGRhdGFzZXQKc3RhdGlvbl9sb2MgPC0gWTE2UTQgJT4lIGRwbHlyOjpzZWxlY3Qoc3RhcnRfc3RhdGlvbl9pZCwgc3RhcnRfbGF0LCBzdGFydF9sb24pICU+JSB1bmlxdWUKIyBjcmVhdGUgYSBkYXRhZnJhbWUgdGhhdCBpbmNsdWRlcyB0aGUgcmVsZXZhbnQgY29sdW1ucyBmb3IgYWxsIGFjdGl2ZSBzdGF0aW9ucyBieSBsZWZ0IGpvaW5pbmcgb24gdGhlIGFjdGl2ZSBzdGF0aW9ucyBhbmQgYWxsIHN0YXRpb25zLCBpZ25vcmluZyBhbnkgcm93cyB0aGF0IGNvbnRhaW4gbWlzc2luZyB2YWx1ZXMKc3RhdGlvbl9sb2MgPC0gZHBseXI6OmxlZnRfam9pbihzdGF0aW9uX2xvYywgaW5kZWdvX3N0YXRpb25zLCBieSA9IGMoInN0YXJ0X3N0YXRpb25faWQiID0gIlN0YXRpb24uSUQiKSkgJT4lIG5hLm9taXQgJT4lIC5bLCAtNl0KIyByZW5hbWUgY29sdW1ucyBvZiBkYXRhZnJhbWUKY29sbmFtZXMoc3RhdGlvbl9sb2MpIDwtIGMoImlkIiwgImxhdCIsICJsb25nIiwgIm5hbWUiLCAiZGF0ZSIpCiMgcmVtb3ZlIHdoaXRlc3BhY2VzIGluIHRoZSBkYXRlIGNvbHVtbgpzdGF0aW9uX2xvYyRkYXRlID0gdHJpbShzdGF0aW9uX2xvYyRkYXRlICU+JSBhcy5jaGFyYWN0ZXIoKSkKCiMgYmVsb3cgYXJlIHR3byBhdXhpbGlhcnkgZnVjdGlvbiB0byBwYXJzZSB0aGUgc3RhcnQvZW5kIHRpbWUgYW5kIGRhdGUgb2YgZWFjaCB0cmlwLCB3aGljaCBkaXNwbGF5cyB0aGVtIGFzIGEgc2luZ2xlIHN0cmluZy4gV2UgcGFydGl0aW9uIHRoZW0gaW50byBzZXBhcmF0ZSBjb2x1bW5zIHRoYXQgbm93IGRpc3BsYXkgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlcywgYW5kIGRhdGUgKFlZWVktTU0tREQpLgoKIyBmb3IgcXVhcnRlcnMgb2YgMjAxNQpwYXJzZVRpbWVfMTUgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiRzX2hvdXIgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlSCIpCiAgZGYkc19taW4gPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkZV9ob3VyIDwtIGZvcm1hdChhcy5QT1NJWGN0KHN0cnB0aW1lKGRmJGVuZF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVIIikKICBkZiRlX21pbiA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRlbmRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkeWVhciA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZIikKICBkZiRtb250aCA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVtIikKICBkZiRkYXkgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlZCIpCiAgZGYkZGF0ZSA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZLSVtLSVkIikKICByZXR1cm4oZGYpCn0KCiMgZm9yIHF1YXJ0ZXJzIG9mIDIwMTYsIHdoaWNoIGFyZSBkZWZpbmVkIGRpZmZlcmVudGx5IGZyb20gMjAxNQpwYXJzZVRpbWVfMTYgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiRzX2hvdXIgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlSCIpCiAgZGYkc19taW4gPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkZV9ob3VyIDwtIGZvcm1hdChhcy5QT1NJWGN0KHN0cnB0aW1lKGRmJGVuZF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVIIikKICBkZiRlX21pbiA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRlbmRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkeWVhciA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZIikKICBkZiRtb250aCA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVtIikKICBkZiRkYXkgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlZCIpCiAgZGYkZGF0ZSA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZLSVtLSVkIikKICByZXR1cm4oZGYpCn0KCiMgcGFzcyBpbnRvIHJlc3BlY3RpdmUgYXV4aWxpYXJ5IGZ1bmN0aW5vcwpZMTVRMiA8LSBwYXJzZVRpbWVfMTUoWTE1UTIpClkxNVEzIDwtIHBhcnNlVGltZV8xNShZMTVRMykKWTE1UTQgPC0gcGFyc2VUaW1lXzE1KFkxNVE0KQpZMTZRMSA8LSBwYXJzZVRpbWVfMTYoWTE2UTEpClkxNlEyIDwtIHBhcnNlVGltZV8xNihZMTZRMikKWTE2UTMgPC0gcGFyc2VUaW1lXzE2KFkxNlEzKQpZMTZRNCA8LSBwYXJzZVRpbWVfMTYoWTE2UTQpCgojIGNvbWJpbmUgYWxsIHF1YXJ0ZXJseSBkYXRhIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBjYWxsZWQgZGYKZGYgPC0gcmJpbmQoWTE1UTIsIFkxNVEzLCBZMTVRNCwgWTE2UTEsIFkxNlEyLCBZMTZRMywgWTE2UTQpCgojIHN0YW5kYXJkaXplIGR1cmF0aW9uIGludG8gdW5pdHMgb2Ygc2Vjb25kcwojIGFsc28gcmVtb3ZlcyBjb2x1bW5zIG5vdCBuZWVkZWQgaW4gc3Vic2VxdWVudCBhbmFseXNlcwpkYXRhQ2xlYW4gPC0gZnVuY3Rpb24oZGYpIHsKICBkZiRkdXJhdGlvbiA8LSBkZiRkdXJhdGlvbiAvIDYwCiAgZGZbLCAtYygxLCAzLCA0LCA2LCA3LCA5LCAxMCwgMTIpXQp9CmRmIDwtIGRhdGFDbGVhbihkZikKCiMgcmVtb3ZlIGluZGl2aWR1YWwgZGF0YWZyYW1lcwpybShZMTVRMiwgWTE1UTMsIFkxNVE0LCBZMTZRMSwgWTE2UTIsIFkxNlEzLCBZMTZRNCkKYGBgCgpEdXJpbmcgb3VyIGluaXRpYWwgZXhwbG9yYXRpb24gb2YgdGhlIGRhdGFzZXQsIHdlIHJlYWxpemVkIHNvbWUgb2YgdGhlIHJlc3BlY3RpdmUgZGF0YXNldHMgZm9yIHRoZSBxdWFydGVybHkgZGF0YSBjb250YWluZWQgbWlzc2luZyB2YWx1ZXMsIHdoaWNoIHdvdWxkIGltcGx5IHRoYXQgb3VyIGZpbmFsIGRhdGFmcmFtZSBhbHNvIGNvbnRhaW5zIG1pc3NpbmcgdmFsdWVzLiBXZSB0aHVzIHRyeSB0byBxdWFsaXRhdGl2ZWx5IHZpc3VhbGl6ZSB0aGVzZSBmZWF0dXJlcywgc2hvdyBiZWxvdzoKCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9CiMgaWdub3JlIGNvbHVtbnMgYWZ0ZXIgcGFzc2hvbGRlcl90eXBlIChjb2xfbnVtID0gMTQpIGFzIHN1YnNlcXVlbnQgY29sdW1ucyBhcmUgZGVmaW5lZCBieSB1cy4KbGlicmFyeShleHRyYWNhdCkKdmlzbmEoZGZbLGMoMToxNCldKQpgYGAKCmBgYHtyfQptaXNzaW5nX3RhYmxlID0gdGFibGUoaXMubmEoZGYpKQpgYGAKCmBgYHtyfQpjYXQoIlBlcmNlbnRhZ2Ugb2YgbWlzc2luZyBlbnRyaWVzOiIsIG1pc3NpbmdfdGFibGVbMl0vc3VtKG1pc3NpbmdfdGFibGUpKjEwMCkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBtaXNzaW5nIHZhbHVlcyBpcyBtaW5pc2N1bGUgcmVsYXRpdmUgdG8gb3VyIHdob2xlIGRhdGFzZXQgKGFwcHJveGltYXRlbHkgMC4wMTcyJSkuIE1vcmVvdmVyLCB0aGV5IGFyZSBhbGwgcmVsYXRlZCB0byBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGRhdGEgYXMgd2VsbCBhcyB0aGUgYmlrZSBJRC4gSWYgbmVlZCBiZSwgd2UgY2FuIGluZmVyIHRoZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGZyb20gdGhlIHN0YXRpb24gbmFtZSBvciBzdGF0aW9uIElELCBidXQgd2UgYWN0aXZlbHkgY2hvc2UgdG8gaWdub3JlIHJvd3MgY29ycmVzcG9uZGluZyB0byB0aGVzZSBtaXNzaW5nIHZhbHVlcyBzaW5jZSB3ZSBjYW5ub3QgZXhwbGljYXRlIHdoeSB0aGVzZSB2YWx1ZXMgYXJlIG1pc3NpbmcuIFdlIHRodXMgY2hvb3NlIHRvIGlnbm9yZSB0aGVtIGFsdG9nZXRoZXIuCgojIyMjIEluZGVnbyBzdGF0aW9ucyBkYXRhCgpGb3IgdGhlIGRhdGEgZGV0YWlsaW5nIHRoZSBpbmZvcm1hdGlvbiBvZiBlYWNoIHN0YXRpb24gKGluZGVnb19zdGF0aW9ucy5jc3YpLCB3ZSBoYWQgdG8gcGVyZm9ybSBhIGJpdCBvZiBkYXRhIHByZXByb2Nlc3NpbmcgdG8gZ2V0IGl0IGludG8gYW4gaWRlYWwgZm9ybWF0LiBUaGUgZGF0YSBjb250YWluZWQgdGhlIGNvbHVtbnMgYXMgZm9sbG93czoKCiogU3RhdGlvbiBJRCAodW5pcXVlIGlkZW50aWZpZXIgb2YgYSBJbmRlZ28gc3RhdGlvbikKKiBTdGF0aW9uIE5hbWUgKG5hbWUgb2YgY29ycmVzcG9uZGluZyBJbmRlZ28gc3RhdGlvbiwgdXN1YWxseSBhIHN0cmVldCBhZGRyZXNzKQoqIEdvIGxpdmUgZGF0ZSAodGhlIGRhdGUgdGhhdCB0aGUgc3RhdGlvbiBzdGFydGVkIG9wZXJhdGlvbnMpCiogU3RhdHVzIChpdHMgY3VycmVudCBzdGF0dXMsIGVpdGhlciBhY3RpdmUgb3IgaW5hY3RpdmUpCgpUaGVyZSB3ZXJlIG1hbnkgbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZXMgaW4gdGhlIDxpPlN0YXRpb24gTmFtZTwvaT4gY29sdW1uLCBzbyBwZXJmb3JtZWQgdGhlIGZvbGxvd2luZyB0byByZW1vdmUgdGhlc2UgdW5uZWVkZWQgY2hhcmFjdGVyczoKCmBgYHtyfQojIGxvYWQgc3RhdGlvbnMgZGF0YQppbmRlZ29fc3RhdGlvbnMgPC0gcmVhZC5jc3YoIi4uL2RhdGEvaW5kZWdvX3N0YXRpb25zLmNzdiIsIGhlYWRlcj1UKQojIGEgZnVuY3Rpb24gdGhhdCByZW1vdmVzIGxlYWRpbmcgYW5kIHRyYWlsaW5nIHdoaXRlIHNwYWNlcyBvZiBhIGdpdmVuIHN0cmluZwp0cmltIDwtIGZ1bmN0aW9uICh4KSBnc3ViKCJeXFxzK3xcXHMrJCIsICIiLCB4KQojIHJlbW92ZSB3aGl0ZXNwYWNlcyBzdXJyb3VuZGluZyBlYWNoIGVudHJ5IGluIHRoZSBjb2x1bW4gb2YgU3RhdGlvbi5OYW1lCmluZGVnb19zdGF0aW9ucyRTdGF0aW9uLk5hbWUgPC0gdHJpbShpbmRlZ29fc3RhdGlvbnMkU3RhdGlvbi5OYW1lKQpgYGAKClRoZXJlIHdlcmUgbm8gbWlzc2luZyB2YWx1ZXMgaW4gdGhpcyB0YWJsZSwgc28gd2UgZG8gbm90IHByb3ZpZGUgcGxvdHMgcmVsYXRlZCB0byB0aGlzIGhlcmUuCgojIyMjIEdlb0pTT04KCkZvciByZWFsLXRpbWUgc3RhdHVzIHVwZGF0ZXMgb2YgSW5kZWdvIHN0YXRpb25zLCB3ZSB1c2UgdGhlIHNlbWktc3RydWN0dXJlZCBbR2VvSlNPTiBkYXRhc2V0XShodHRwczovL3d3dy5yaWRlaW5kZWdvLmNvbS9zdGF0aW9ucy9qc29uLykgcHJvdmlkZWQgYnkgSW5kZWdvLiBXZSBhbGxvdyB0aGUgdXNlciB0byBtYW51YWxseSByZWZyZXNoIG91ciBTaGlueSBhcHAsIGluIHdoaWNoIHdlIGNhbGwgdGhlIEdlb0pTT04gZmlsZSBpbiByZWFsLXRpbWUgdG8gZ2V0IGEgdmlzdWFsaXphdGlvbiB1cGRhdGUgKG1vcmUgc3BlY2lmaWMgZGV0YWlscyBpbiBzdWJzZXF1ZW50IHNlY3Rpb25zKS4gV2UgdXNlZCB0aGUgUiBwYWNrcGFnZSBbZ2VvanNvbmlvXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2VvanNvbmlvL2luZGV4Lmh0bWwpIHRvIHVucGFjayBhbmQgdHJhbnNmb3JtIHRoaXMgZGF0YSBpbnRvIGEgd29ya2FibGUgZm9ybWF0IGFzIGZvbGxvd3M6CgpgYGB7cn0KbGlicmFyeShnZW9qc29uaW8pCnN0YXRpb25fc3RhdHVzIDwtIGZyb21KU09OKCJodHRwczovL2diZnMuYmN5Y2xlLmNvbS9iY3ljbGVfaW5kZWdvL3N0YXRpb25fc3RhdHVzLmpzb24iKQpgYGAKCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5CgoKVGhlIHByaW1hcnkgb2JqZWN0aXZlIG9mIHRoaXMgcmVwb3J0IGlzIHRvIGRlcml2ZSB2YWx1YWJsZSBpbnNpZ2h0cyBvZiBkZXBsb3lpbmcgc2hhcmVkIGJpa2VzIGZvciBiaWcgY2l0aWVzIChpbiBvdXIgY2FzZSwgUGhpbGFkZWxwaGlhJ3MgSW5kZWdvKSwgYW5kIGhvdyB0aGV5IGFyZSB1dGlsaXplZCBieSB0aGUgcHVibGljIG1hc3MuIE1vcmUgc3BlY2lmaWNhbGx5LCB3ZSB3YW50IHRvIHVuZGVyc3RhbmQgcGF0dGVybnMgb2YgdXNhZ2UgYW1vbmcgZGlmZmVyZW50IHN0YXRpb25zIGFuZCB1c2VycyBvdmVyIHRpbWUuIFRoZSBleHBsb3JhdG9yeSBtZXRob2RvbG9naWVzIHRoYXQgd2UgaW1wbGVtZW50IG9uIHRoZSBJbmRlZ28gZGF0YXNldCB3aWxsIGdlbmVyYWxseSBmb2xsb3cgY29udmVudGlvbmFsIHByaW5jaXBsZXMgb2YgYW5hbHl6aW5nIHRlbXBvcmFsIGFuZCBzcGF0aWFsIGZlYXR1cmVzIG9mIGxhcmdlIGRhdGFzZXRzLiAKClVwb24gcmV0cmlldmluZyB0aGUgZGF0YXNldCwgd2UgaW1tZWRpYXRlbHkgcmFpc2VkIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zOgoKMS4gRm9yIHRoZSB0b3RhbCAxMDUgc3RhdGlvbnMgZGVwbG95ZWQgZ3JhZHVhbGx5IHdpdGhpbiB0d28geWVhcnMsIHdoaWNoIHN0YXRpb25zIGFyZSB0aGUgbW9zdCBwb3B1bGFyIG9uZXM/IEhhcyB0aGUgcG9wdWxhcml0eSBjaGFuZ2VkIG92ZXIgdGltZT8KCjIuIEZvciBzcGVjaWZpYyBzdGF0aW9ucyB3ZSBoYXZlIGFuIGV4cGVjdGlvbiBvbiB0aGVpciBkYWlseSByZW50YWwgcGF0dGVybnMuIEhvdyBkbyB3ZSB2aXN1YWxpemUgYW5kIGV4YW1pbmUgc3VjaCBwYXR0ZXJucz8KCjMuIFdpdGggdGhlIGdlb2dyYXBoaWNhbCBkYXRhIG9mIHN0YXRpb25zIGFuZCBHZW9TT04gZmlsZSwgaG93IGRvIHdlIGRyYXcgY29ubmVjdGlvbnMgYW1vbmcgc3RhdGlvbnMgYW5kIHNlZSByZW50YWwgYW5kIHJldHVybiBwYXR0ZXJucywgcG9wdWxhciByb3V0ZXMsIGFuZCB2aXN1YWxpemUgdGhlIHJlYWwtdGltZSBzdGF0aW9uIHN0YXR1cz8KCldlIHByZXNlbnQgaGVyZSBhIHF1aWNrIHJ1bi10aHJvdWdoIG9mIHRoZSBwbG90cyB3ZSBwcmVzZW50IGluIG91ciBtYWluIGFuYWx5c2lzLCB3aGVyZSB3ZSB3aWxsIGRpdmUgaW50byBtdWNoIGZpbmVyIGRldGFpbHMuIFRoZSBmb2xsb3dpbmcgcGxvdHMgZGVtb25zdHJhdGUgdGhlIG1haW4gZmluZGluZ3Mgb2Ygb3VyIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMuCgpGaXJzdGx5LCB3ZSBtYWtlIGdlbmVyYWwgbGluZSBwbG90cyB3aXRoIG1vbnRobHkgZnJlcXVlbmN5IGRhdGEgYW5kIGZ1cnRoZXIgc2VwYXJhdGUgdGhlIGRhdGEgaW50byB3ZWVrIGRheXMgYW5kIHdlZWtlbmRzLiBGcm9tIHRoZSBwbG90cywgd2Ugc2VlIGNsZWFybHkgdGhlIHNlYXNvbmFsIHVzYWdlIHZhcmlhdGlvbi4KIVtdKC9Vc2Vycy9tYXNvbnN1bi9EZXNrdG9wL1NUQVQgNTcwMi9wcm9qZWN0L3BpYy9mcmVxLnBuZykKClNlY29uZGx5LCB3ZSBwaWNrIHRocmVlIGRpZmZlcmVudCBzdGF0aW9ucyBsb2NhdGVkIGF0IHRoZSB1bml2ZXJzaXR5IGFyZWEsIGRvd250b3duIGFuZCBzdWJ1cmJhbiBQaGlsYWRlbHBoaWEsIGFuZCBkcmF3IHRoZSBhdmVyYWdlIGhvdXJseSB0cmlwIGR1cmF0aW9ucyBmb3Igd2VlayBkYXlzIGFuZCB3ZWVrZW5kcyBpbiBhIHNlbGVjdGVkIHRpbWUgcGVyaW9kLiBUcmF2ZWwgcGF0dGVybnMgYW1vbmcgZGlmZmVyZW50IHR5cGVzIG9mIHN0YXRpb25zIHRodXMgYmVjb21lcyBldmlkZW50LiBGb3IgZXhhbXBsZSwgdGhlIHdlZWtlbmRzJyBhdmVyYWdlIHRyaXAgZHVyYXRpb24gaW4gdGhlIGFmdGVybm9vbiBpcyBsb25nZXIgdGhhbiB0aGF0IG9mIHRoZSB3ZWVrZGF5J3MgZm9yIHVzZXJzIGNsb3NlIHRvIHVuaXZlcnNpdHkgY2FtcHVzZXMuCiFbXSgvVXNlcnMvbWFzb25zdW4vRGVza3RvcC9TVEFUIDU3MDIvcHJvamVjdC9zdGF0aW9ucy5wbmcpCgpUaGlyZGx5LCB3ZSB1dGlsaXplIGBsZWFmbGV0YCB0byBjcmVhdGUgaW50ZXJhY3RpdmUgbWFwcyBpbiBvcmRlciB0byByZXByZXNlbnQgdGhlIHNwYXRpYWwgaW5mb3JtYXRpb24gd2UgY2FuIGRlcml2ZSBmcm9tIHRoZSBkYXRhc2V0LiBXZSBlZmZlY3RpdmVseSByZXByZXNlbnRlZCB0aGUgYXZlcmFnZSBkYWlseSByZW50YWwgYW5kIHJldHVybiBmcmVxdWVuY2llcyBhbW9uZyBzdGF0aW9ucyB1c2luZyBkaWZmZXJlbnQgZG90IHNpemVzIChsYXJnZXIgZG90cyBpbXBsaWVzIGhpZ2hlciBmcmVxdWVuY3kpLiBXZSBlbWJlZCB0aGUgcmVhbC10aW1lIEdlb0pTT04gZmlsZSBpbnRvIG91ciBgU2hpbnlgIGFwcCB0byBzaG93IHJlYWwtdGltZSB1cGRhdGVzIG9mIHN0YXRpb24gc3RhdHVzLiBJbiBvcmRlciB0byBqb2ludGx5IGFuYWx5emUgdGhlIHNwYXRpYWwgYW5kIHRpbWUgZGltZW5zaW9ucywgd2UgcGljayB0aGUgdGVuIG1vc3QgcG9wdWxhciByZW50YWwgYW5kIHJldHVybiBzdGF0aW9ucyBmb3IgdHdvIHJlY2VudCBxdWFydGVycyB0byBkZXRlcm1pbmUgZGlmZmVyZW5jZXMgaW4gdHJlbmRzLiBCZXNpZGVzIHRoZSBwbG90cyBiZWxvdywgd2UgYWxzbyBtYWRlIGEgbWFwIGZvciB0aGUgZml2ZSBtb3N0IHBvcHVsYXIgcm91dGVzIChzZWUgbmV4dCBwYXJ0KS4KIVtdKC9Vc2Vycy9tYXNvbnN1bi9EZXNrdG9wL1NUQVQgNTcwMi9wcm9qZWN0L3BpYy9tYXAucG5nKQoKTW9yZW92ZXIsIHdlIGFuYWx5emUgdGhlIGRhaWx5IHRyaXAgZHVyYXRpb25zIGFuZCB0aGUgdXNlciBwcm9maWxlcywgYW5kIHVuY292ZXIgdGhlIHRoZSBzaGlmdCBvZiBwYXNzaG9sZGVyIHR5cGVzIGFuZCB0cmlwIHJvdXRlIChvbmUtd2F5IG9yIHJvdW5kLXRyaXApIGNoYW5nZXMuIFRoZSBtb3N0IGluY2lzaXZlIG9ic2VydmF0aW9uIHdlIGdvdCBmcm9tIHRoaXMgc2VjdGlvbiBpcyB0aGF0IHRoZSBwZXJjZW50YWdlIG9mIHRoZSBtb3N0IHBvcHVsYXIgcGFzc2hvbGRlciB0eXBlIChJbmRlZ28zMCkgYW5kIG9uZS13YXkgdHJpcHMgaGFzIGdvdHRlbiBtb3JlIHBvcHVsYXIuIEluIGFkZGl0aW9uLCBsZXNzIHBlb3BsZSBhcmUgdGFraW5nIHJvdW5kIHRyaXBzLgoKIVtdKC9Vc2Vycy9tYXNvbnN1bi9EZXNrdG9wL1NUQVQgNTcwMi9wcm9qZWN0L3BpYy90eXBlLnBuZykKCkxhc3RseSwgd2UgY3JlYXRlIGEgYFNoaW55YCBkYXNoYm9hcmQgdG8gbGV0IHBlb3BsZSBleHBsb3JlIHRoZSBkYXRhc2V0IGFzIHRoZWlyIHdpc2hlcy4gVGhhdCBpcywgdGhleSBjYW4gc2VsZWN0IHN0YXRpb25zIGFuZCBkYXRlIHJhbmdlcyBvZiBpbnRlcmVzdCwgYW5kIGFsc28gbW9kaWZ5IHBhcmFtZXRlcnMgb2YgZGlmZmVyZW50IGNoYXJ0cyAoZS5nLiBudW1iZXIgb2YgYmlucyBmb3IgaGlzdG9ncmFtcykgdG8gZ2VuZXJhdGUgY3VzdG9taXplZCBpbnNpZ2h0cy4gCiFbXSgvVXNlcnMvbWFzb25zdW4vRGVza3RvcC9TVEFUIDU3MDIvcHJvamVjdC9waWMvc2hpbnkucG5nKQoKIyMgTWFpbiBhbmFseXNpcyAKCkluIHRoaXMgbWFpbiBzZWN0aW9uLCB3ZSBwcmVzZW50IG91ciBmaW5kaW5ncyBvZiBkaWZmZXJlbnQgZmFjZXRzIHBlcnRhaW5pbmcgdG8gdGhlIEluZGVnbyBkYXRhc2V0LiBXZSBzdWJkaXZpZGUgb3VyIGFuYWx5c2lzIGludG8gZm91ciBpbnRlcnJlbGF0ZWQgc2VjdGlvbnM6IHN0YXRpb24gdXNhZ2UsIGRhaWx5IHRyYWZmaWMsIHRyaXAgZHVyYXRpb24sIGFuZCB1c2VyIHByb2ZpbGVzLCBpbiB0aGF0IHBhcnRpY3VhbHIgb3JkZXIuIFdlIHdpbGwgd2FsayB0aGUgcmVhZGVyIHRocm91Z2ggb3VyIHRob3VnaHQgcHJvY2VzcyBhbmQgZmluZGluZ3MsIGFuZCBmaW5hbGx5IHByZXNlbnQgc29tZSBjb25jbHVkaW5nIHJlbWFya3MuCgojIyMjIFN0YXRpb24gdXNhZ2UKCkluIHRoaXMgc2VjdGlvbiwgd2UgZXhhbWluZSB0aGUgdXNhZ2UgcmF0ZSBhbW9uZyB0aGUgZGlmZmVyZW50IEluZGVnbyBzdGF0aW9ucyBhY3Jvc3MgUGhpbGFkZWxwaGlhLiBXZSBtb3RpdmF0ZSBvdXIgYW5hbHlzaXMgZnJvbSBhIGhvbGlzdGljIHBlcnNwZWN0aXZlIHRvd2FyZHMgbW9yZSBncmFudWxhciBhbmFseXNpcy4gVGhpcyBzdHJ1Y3R1cmFsIGxheW91dCBhbHNvIGhvbGRzIHRydWUgZm9yIHN1YnNlcXVlbnQgc2VjdGlvbnMuIFdpdGggdGhhdCBpbiBtaW5kLCBsZXQgdXMgZmlyc3QgcGF0dGVybnMgcmVsYXRlZCB0byBzb21lIG9mIHRoZSBtb3JlIHBvcHVsYXIgSW5kZWdvIHN0YXRpb25zLiBXZSBzdWJkaXZpZGUgb3VyIGFuYWx5c2lzIHRvIHRoYXQgb2YgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMsIHdoaWNoIHJlZmxlY3QgcG9wdWxhciBzdGFydGluZyBhbmQgdGVybWluYWwgZGVzdGluYXRpb25zIHRoYXQgcGVvcGxlIGVsZWN0IHRvIGNob29zZSBpbiBQaGlsYWRlbHBoaWEuCgpgYGB7ciByZXN1bHRzPSJoaWRlIixmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00fQojIFBvcHVsYXIgU3RhdGlvbnMgLSBoaXN0b2dyYW0KbGlicmFyeShncmlkRXh0cmEpCnBvcHVsYXJfc3RhdGlvbiA8LSBkZiAlPiUgZ3JvdXBfYnkoeWVhciwgbW9udGgsIHN0YXJ0X3N0YXRpb25faWQpICU+JSBzdW1tYXJpemUocmVudGFsID0gbigpKQpjb2xuYW1lcyhwb3B1bGFyX3N0YXRpb24pWzNdIDwtICJpZCIKcG9wdWxhcl9zdGF0aW9uJGlkIDwtIGFzLmNoYXJhY3Rlcihwb3B1bGFyX3N0YXRpb24kaWQpCmZvbyA8LSBkZiAlPiUgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGVuZF9zdGF0aW9uX2lkKSAlPiUgc3VtbWFyaXplKHJldHVybiA9IG4oKSkKY29sbmFtZXMoZm9vKVszXSA8LSAiaWQiCmZvbyRpZCA8LSBhcy5jaGFyYWN0ZXIoZm9vJGlkKQoKcG9wdWxhcl9zdGF0aW9uIDwtIHBvcHVsYXJfc3RhdGlvbiAlPiUgZnVsbF9qb2luKGZvbywgYnkgPSBjKCJpZCIgPSAiaWQiLCAieWVhciIgPSAieWVhciIsICJtb250aCIgPSAibW9udGgiKSkKcm0oZm9vKQpwb3B1bGFyX3N0YXRpb24gPC0gdGlkeXI6OmdhdGhlcihwb3B1bGFyX3N0YXRpb24sIHR5cGUsIGNvdW50LCBjKDQsNSkpCmNvbG5hbWVzKHBvcHVsYXJfc3RhdGlvbilbM10gPC0gImlkIgpwb3B1bGFyX3N0YXRpb25baXMubmEocG9wdWxhcl9zdGF0aW9uKV0gPC0gMApwb3B1bGFyX3N0YXRpb24gPC0gcG9wdWxhcl9zdGF0aW9uW3BvcHVsYXJfc3RhdGlvbiRpZCE9IiNOL0EiLF0Kc3RhdGlvbl9sb2MkaWQgPC0gYXMuY2hhcmFjdGVyKHN0YXRpb25fbG9jJGlkKQpyZWR1Y2VkX3N0YXRpb24gPC0gcG9wdWxhcl9zdGF0aW9uICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKcmVkdWNlZF9zdGF0aW9uJHF1YXJ0ZXIgPC0gKGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHJlZHVjZWRfc3RhdGlvbiRtb250aCkpIC0gMSkgJS8lIDMgKyAxCnJlZHVjZWRfc3RhdGlvbiA8LSByZWR1Y2VkX3N0YXRpb24gJT4lIGdyb3VwX2J5KGlkLCBuYW1lLCBsYXQsIGxvbmcsIHR5cGUsIHllYXIsIHF1YXJ0ZXIpICU+JSBzdW1tYXJpemUoY291bnQgPSBzdW0oY291bnQpKQoKIyBFYXJsaWVzdCBxdWFydGVyOiAyMDE1UTIsIExhdGVzdCBxdWFydGVyOiAyMDE2UTQKcmVkdWNlZF9zdGF0aW9uJHllYXIgPC0gYXMubnVtZXJpYyhyZWR1Y2VkX3N0YXRpb24keWVhcikKcnNfZmlyc3RfcmVudGFsIDwtIHJlZHVjZWRfc3RhdGlvbltyZWR1Y2VkX3N0YXRpb24keWVhcj09MjAxNSZyZWR1Y2VkX3N0YXRpb24kcXVhcnRlcj09MiZyZWR1Y2VkX3N0YXRpb24kdHlwZT09InJlbnRhbCIsXQpyc19sYXN0X3JlbnRhbCA8LSByZWR1Y2VkX3N0YXRpb25bcmVkdWNlZF9zdGF0aW9uJHllYXI9PTIwMTYmcmVkdWNlZF9zdGF0aW9uJHF1YXJ0ZXI9PTQmcmVkdWNlZF9zdGF0aW9uJHR5cGU9PSJyZW50YWwiLF0KcnNfZmlyc3RfcmV0dXJuIDwtIHJlZHVjZWRfc3RhdGlvbltyZWR1Y2VkX3N0YXRpb24keWVhcj09MjAxNSZyZWR1Y2VkX3N0YXRpb24kcXVhcnRlcj09MiZyZWR1Y2VkX3N0YXRpb24kdHlwZT09InJldHVybiIsXQpyc19sYXN0X3JldHVybiA8LSByZWR1Y2VkX3N0YXRpb25bcmVkdWNlZF9zdGF0aW9uJHllYXI9PTIwMTYmcmVkdWNlZF9zdGF0aW9uJHF1YXJ0ZXI9PTQmcmVkdWNlZF9zdGF0aW9uJHR5cGU9PSJyZXR1cm4iLF0KCnJzX2ZpcnN0X3JlbnRhbCA8LSByc19maXJzdF9yZW50YWxbb3JkZXIoLXJzX2ZpcnN0X3JlbnRhbCRjb3VudCksXVsxOjEwLF0KcnNfbGFzdF9yZW50YWwgPC0gcnNfbGFzdF9yZW50YWxbb3JkZXIoLXJzX2xhc3RfcmVudGFsJGNvdW50KSxdWzE6MTAsXQpyc19maXJzdF9yZXR1cm4gPC0gcnNfZmlyc3RfcmV0dXJuW29yZGVyKC1yc19maXJzdF9yZXR1cm4kY291bnQpLF1bMToxMCxdCnJzX2xhc3RfcmV0dXJuIDwtIHJzX2xhc3RfcmV0dXJuW29yZGVyKC1yc19sYXN0X3JldHVybiRjb3VudCksXVsxOjEwLF0KCnJzX2ZpcnN0X3JlbnRhbCRuYW1lIDwtIGZhY3Rvcihyc19maXJzdF9yZW50YWwkbmFtZSwgbGV2ZWxzID0gcnNfZmlyc3RfcmVudGFsJG5hbWVbb3JkZXIocnNfZmlyc3RfcmVudGFsJGNvdW50KV0pCnJzX2xhc3RfcmVudGFsJG5hbWUgPC0gZmFjdG9yKHJzX2xhc3RfcmVudGFsJG5hbWUsIGxldmVscyA9IHJzX2xhc3RfcmVudGFsJG5hbWVbb3JkZXIocnNfbGFzdF9yZW50YWwkY291bnQpXSkKcnNfZmlyc3RfcmV0dXJuJG5hbWUgPC0gZmFjdG9yKHJzX2ZpcnN0X3JldHVybiRuYW1lLCBsZXZlbHMgPSByc19maXJzdF9yZXR1cm4kbmFtZVtvcmRlcihyc19maXJzdF9yZXR1cm4kY291bnQpXSkKcnNfbGFzdF9yZXR1cm4kbmFtZSA8LSBmYWN0b3IocnNfbGFzdF9yZXR1cm4kbmFtZSwgbGV2ZWxzID0gcnNfbGFzdF9yZXR1cm4kbmFtZVtvcmRlcihyc19sYXN0X3JldHVybiRjb3VudCldKQoKcDEgPC0gZ2dwbG90KHJzX2ZpcnN0X3JlbnRhbCwgYWVzKHggPSBuYW1lLCB5ID0gY291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIGNvbG9yPSJibHVlIiwgZmlsbD0iYmx1ZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50KSwgaGp1c3Q9LTAuMDUsIHNpemU9Mi41KSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJNb3N0IFBvcHVsYXIgUmVudGFsIFN0YXRpb24gMjAxNVEyIikgKwogIHhsYWIoIkZyZXF1ZW5jeSIpICsKICB5bGFiKCJTdGF0aW9uIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiY3lsIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkKCnAyIDwtIGdncGxvdChyc19sYXN0X3JlbnRhbCwgYWVzKHggPSBuYW1lLCB5ID0gY291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIGNvbG9yPSJibHVlIiwgZmlsbD0iYmx1ZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50KSwgaGp1c3Q9LTAuMDUsIHNpemU9Mi41KSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJNb3N0IFBvcHVsYXIgUmVudGFsIFN0YXRpb24gMjAxNlE0IikgKwogIHhsYWIoIkZyZXF1ZW5jeSIpICsKICB5bGFiKCJTdGF0aW9uIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiY3lsIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkKCnAzIDwtIGdncGxvdChyc19maXJzdF9yZXR1cm4sIGFlcyh4ID0gbmFtZSwgeSA9IGNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBjb2xvcj0iYmx1ZSIsIGZpbGw9ImJsdWUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCksIGhqdXN0PS0wLjA1LCBzaXplPTIuNSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgiTW9zdCBQb3B1bGFyIFJldHVybiBTdGF0aW9uIDIwMTVRMiIpICsKICB4bGFiKCJGcmVxdWVuY3kiKSArCiAgeWxhYigiU3RhdGlvbiIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoImN5bCIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpCgpwNCA8LSBnZ3Bsb3QocnNfbGFzdF9yZXR1cm4sIGFlcyh4ID0gbmFtZSwgeSA9IGNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBjb2xvcj0iYmx1ZSIsIGZpbGw9ImJsdWUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCksIGhqdXN0PS0wLjA1LCBzaXplPTIuNSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgiTW9zdCBQb3B1bGFyIFJldHVybiBTdGF0aW9uIDIwMTZRNCIpICsKICB4bGFiKCJGcmVxdWVuY3kiKSArCiAgeWxhYigiU3RhdGlvbiIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoImN5bCIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpCgpwMSA8LSBnZ3Bsb3RfZ3RhYmxlKGdncGxvdF9idWlsZChwMSkpCnAyIDwtIGdncGxvdF9ndGFibGUoZ2dwbG90X2J1aWxkKHAyKSkKcDMgPC0gZ2dwbG90X2d0YWJsZShnZ3Bsb3RfYnVpbGQocDMpKQpwNCA8LSBnZ3Bsb3RfZ3RhYmxlKGdncGxvdF9idWlsZChwNCkpCgptYXhXaWR0aCA9IGdyaWQ6OnVuaXQucG1heChwMSR3aWR0aHNbMjozXSwgcDIkd2lkdGhzWzI6M10sIHAzJHdpZHRoc1syOjNdLCBwNCR3aWR0aHNbMjozXSkKcDEkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKcDIkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKcDMkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKcDQkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKYGBgCgpgYGB7ciBmaWcud2lkdGg9Ny41LCBmaWcuaGVpZ2h0PTV9CiMgUGxvdCBoaXN0b2dyYW1zIGZvciBzZWxlY3RlZCBxdWFydGVycwpncmlkLmFycmFuZ2UocDEsIHAzLCBwMiwgcDQsIGhlaWdodHMgPSBjKDMsIDMpKQpgYGAKCkluIHRoZSBhYm92ZSBncmFwaCwgd2Ugc2VlIHRoYXQgdGhlIG1vc3QgcG9wdWxhciBzdGF0aW9ucyBhcmUgc29tZXdoYXQgaW52YXJpYW50IHRvIHRpbWUuIFdlIGNob3NlIHRoZSBmaXJzdCBhbmQgbGF0ZXN0IHF1YXJ0ZXJzIHRvIGRldGVybWluZSBtb3N0IHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMuIEluIGFsbCBjYXNlcywgdGhlIG1vc3QgcG9wdWxhciByZW50YWwgYW5kIHJldHVybiBzdGF0aW9uIHdhcyBbUml0dGVuaG91c2UgU3F1YXJlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SaXR0ZW5ob3VzZV9TcXVhcmUpLiBUaGlzIGlzIGludHVpdGl2ZSBhcyBpdCBpcyBub3Qgb25seSBhIHBvcHVsYXIgdG91cmlzdCBkZXN0aW5hdGlvbiwgYmVpbmcgaGFpbGVkIGFzICJvbmUgb2YgdGhlIGZpbmVzdCB1cmJhbiBwdWJsaWMgc3BhY2VzIGluIHRoZSBVbml0ZWQgU3RhdGVzLCIgKGF0IGxlYXN0IGFjY29yZGluZyB0byBXaWtpcGVkaWEpIGJ1dCBpdCBpcyBhbHNvIGluIGNsb3NlIHByb3hpbWl0eSB0byB0aGUgZXBpY2VudGVyIG9mIFBoaWxhZGVscGhpYS4gTW9yZSBzcGVjaWZpY2FsbHksIGluIFEyIG9mIDIwMTUsIHRoZSB0b3AgZml2ZSBtb3N0IHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMgd2VyZSBhbGwgdGhlIHNhbWUsIHdpdGggdGhlIGV4YWN0IHNhbWUgcmFua2luZy4gVGh1cywgaXQgd291bGQgc2VlbSB0aGF0IHRoZXJlIGlzIG5vIHByZWZlcmVuY2UgZm9yIGRyb3BvZmYgb3IgcGlja3VwIHN0YXRpb25zLCBidXQgc2ltcGx5IGh1YnMgZm9yIGJpa2UgdXNlcnMuIEluIFE0IG9mIDIwMTYsIHdlIG9ic2VydmUgbWFueSBvZiB0aGUgc2FtZSByZXR1cm4gYW5kIGFuZCByZW50YWwgc3RhdGlvbnMsIGJ1dCBkaWZmZXJlbmNlcyBleGlzdC4gRm9yIGluc3RhbmNlLCBQaGlsYWRlbHBoaWEgTXVzZXVtIG9mIEFydCAoUE1PQSkgaXMgbm8gbG9uZ2VyIGluIHRoZSB0b3AgdGVuIG1vc3QgcG9wdWxhciByZXR1cm4gb3IgcmVudGFsIHN0YXRpb25zIHdoZXJlYXMgVW5pdmVyc2l0eSBDaXR5IHN0YXRpb24gaXMsIHdoaWNoIHdhcyBub3QgaW4gUTIgb2YgMjAxNS4gVGhlc2UgdmFyaWFiaWxpdHkgYXJlIGRpZmZpY3VsdCB0byBleHBsYWluIGJhc2VkIG9uIHRoZSBkYXRhc2V0IGFsb25lLCBidXQgd2Ugc3VybWlzZSBhIGNvdXBsZSBvZiBwb3NzaWJsZSBjYXVzZXMuIEZpcnN0LCB0aGUgZGVtb2dyYXBoaWMgb2YgSW5kZWdvIHVzZXJzIGhhcyBzaGlmdGVkIG92ZXIgdGltZSAoZnJvbSBpbnRyaWd1ZWQgdXNlcnMgdG8gYSB0aG9zZSB1c2luZyBpdCBmb3Igcm91dGluZSB0cmlwcykuIFNlY29uZCwgc2Vhc29uYWxpdHkgbWF5IGFmZmVjdCBwb3B1bGFyIHJlbnRhbC9yZXR1cm4gc3RhdGlvbnMuIFRoZXJlIGlzIGEgbGFyZ2VyIHRvdXJpc3QgY29ob3J0IGR1cmluZyB0aGUgc3VtbWVyIHNvIHRoYXQgbWF5IGJlIHdoeSBQTU9BIGlzIHNvIGhpZ2ggdXAgaW4gdGhlIHJhbmtpbmdzIGZvciBRMiBvZiAyMDE1LiBUaGVzZSBhcmUgYWxsIGNvbmplY3R1cmFsLCBhbmQgaXQgbWF5IHNlcnZlIGFzIGFuIGludGVyZXN0aW5nIGF2ZW51ZSBmb3IgZnVydGhlciBleHBsb3JhdGlvbi4gSG93ZXZlciB0aGlzIGlzIGJleW9uZCB0aGUgc2NvcGUgb2Ygb3VyIHByb2plY3QuCgpXZSBwb3N0dWxhdGUgdGhhdCB0aGUgcG9wdWxhcml0eSBvZiBjZXJ0YWluIHN0YXRpb25zIGFjcm9zcyB0aW1lIGlzIGR1ZSB0byB0aGUgZmFjdCB0aGV5IGFyZSBpbiBhcmVhcyBvZiBoZWF2eSBwZWRlc3RyaWFuIHRyYWZmaWMgKGUuZy4gY2l0eSBjZW50ZXJzKSwgYW5kIHRoZXkgYXJlIGluIGNsb3NlIHByb3hpbWl0eSB0byBvbmUgYW5vdGhlciBidXQgb3V0c2lkZSBjb21mb3J0YWJsZSB3YWxraW5nIHJhbmdlLiBXZSB0aHVzIHB1dCB0aGlzIGlkZWEgdG8gdGhlIHRlc3QgYnkgY29uc2lkZXJpbmcgc3BhdGlhbCBwYXR0ZXJucyBhbmQgZGlzdGFuY2VzIGFtb25nc3QgdGhlc2UgbW9yZSBwb3B1bGFyIHN0YXRpb25zLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgUG9wdWxhciBzdGF0aW9ucyAtIG1hcHMKcnNfZmlyc3RfcmV0dXJuJHR5cGUgPC0gIlJldHVybiIKcnNfbGFzdF9yZXR1cm4kdHlwZSA8LSAiUmV0dXJuIgpyc19maXJzdF9yZW50YWwkdHlwZSA8LSAiUmVudGFsIgpyc19sYXN0X3JlbnRhbCR0eXBlIDwtICJSZW50YWwiCgpRMlkxNSA8LSByYmluZChyc19maXJzdF9yZXR1cm4sIHJzX2ZpcnN0X3JlbnRhbCkKUTRZMTYgPC0gcmJpbmQocnNfbGFzdF9yZXR1cm4sIHJzX2xhc3RfcmVudGFsKQoKcGFsIDwtIGNvbG9yRmFjdG9yKGMoIm5hdnkiLCAicmVkIiksIGRvbWFpbiA9IGMoIlJldHVybiIsICJSZW50YWwiKSkKCiMgUHVycGxlOiBwb3B1bGFyIGZvciBib3RoIHJldHVybiBhbmQgcmVudGFsCiMgUmVkOiBvbmx5IGZvciByZW50YWwKIyBCbHVlOiBvbmx5IGZvciByZXR1cm4KIyBTaXplOiB0b3RhbCBmcmVxdWVuY3kgd2l0aGluIHRoZSBxdWFydGVyCgojIGZvciBRMiBvZiAyMDE1CmxlYWZsZXQoUTJZMTUpICU+JQogIGFkZFRpbGVzKCkgJT4lCiAgYWRkUHJvdmlkZXJUaWxlcygnQ2FydG9EQi5Qb3NpdHJvbicpICU+JQogIHNldFZpZXcobG5nPS03NS4xNjM3NjMsIGxhdD0zOS45NTI1NjgsIHpvb20gPSAxMykgJT4lCiAgYWRkQ2lyY2xlTWFya2VycygKICAgIHJhZGl1cyA9IH5zcXJ0KGNvdW50KS8xMCwKICAgIGNvbG9yID0gfnBhbCh0eXBlKSwKICAgIHN0cm9rZSA9IEZBTFNFLCBmaWxsT3BhY2l0eSA9IDAuNSwgCiAgICBwb3B1cCA9IH5uYW1lCiAgKQoKIyBmb3IgUTQgb2YgMjAxNgpsZWFmbGV0KFE0WTE2KSAlPiUKICBhZGRUaWxlcygpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMoJ0NhcnRvREIuUG9zaXRyb24nKSAlPiUKICBzZXRWaWV3KGxuZz0tNzUuMTYzNzYzLCBsYXQ9MzkuOTUyNTY4LCB6b29tID0gMTMpICU+JQogIGFkZENpcmNsZU1hcmtlcnMoCiAgICByYWRpdXMgPSB+c3FydChjb3VudCkvMTAsCiAgICBjb2xvciA9IH5wYWwodHlwZSksCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjUsIAogICAgcG9wdXAgPSB+bmFtZQogICkKYGBgCgo8YnI+IEluIHRoZSBhYm92ZSB0d28gaW50ZXJhY3RpdmUgbWFwcywgdGhlIGNvbG9yIHB1cnBsZSByZXByZXNlbnRzIHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMsIHJlZCBmb3IgcmVudGFsLCBhbmQgYmx1ZSBmb3IgcmV0dXJuLiBUaGUgc2l6ZSBvZiB0aGUgZG90IGRlbm90ZXMgdGhlIHRvdGFsIGZyZXF1ZW5jeSBvZiByaWRlcyB3aXRoaW4gdGhlIHF1YXJ0ZXIgKHRvcDogUTIgb2YgMjAxNSwgYm90dG9tOiBib3R0b206IFE0IG9mIDIwMTYpLiBDcnVkZWx5IGluIGxpbmUgd2l0aCBvdXIgaHlwb3RoZXNpcywgbW9zdCBvZiB0aGUgbW9zdCBwb3B1bGFyIHN0YXRpb25zIChieSBkb3Qgc2l6ZSkgYXJlIGNvbmNlbnRyYXRlZCBpbiB0aGUgZXBpY2VudGVyIG9mIFBoaWxhZGVscGhpYSwgd2hpY2ggaXMgcm91Z2hseSBiZXR3ZWVuIFJpdHRlbmhvdXNlIFNxdWFyZSBhbmQgQ2hpbmF0b3duLiBXZSBhbHNvIHNlZSB0aGF0IFBNT0EgaXMgb2ZmIGluIHRoZSBkaXN0YW5jZSB0byB0aGUgbm9ydGhlYXN0LCBidXQgd2FzIGEgcG9wdWxhciByZW50YWwgYW5kIHJldHVybiBzdGF0aW9uIGluIFEyIG9mIDIwMTUuIFRoaXMgYWxzbyBjb3JyZXNwb25kcyB0byBvdXIgdGhlb3J5IHRoYXQgc3VtbWVyIG1hZGUgdGhpcyBhIHBvcHVsYXIgaHViIGZvciBiaWtlIHVzZXJzLiBJbiBRNCBvZiAyMDE2LCB0aGUgc2hpZnQgaW4gY2x1c3RlcmluZyBvZiBwb3B1bGFyIHN0YXRpb25zIHJldm9sdmVzIGFyb3VuZCB0aGUgdHdvIHNob3JlcyBvZiBTY2h1eWxraWxsIFJpdmVyLiBUaGlzIG1heSBjb3Jyb2JvcmF0ZSBvdXIgb2JzZXJ2YXRpb24gdGhhdCBwZW9wbGUgdXNlIGl0IGFzIGEgbWVhbnMgdG8gY3Jvc3MgdGhlIHJpdmVyIGZvciByb3V0aW5lIGNvbW11dGVzIChlLmcuIGJldHdlZW4gVW5pdmVyc2l0eSBDaXR5L1Bvd2VsdG9uIFZpbGxhZ2UgYW5kIFJpdHRlbmhvdXNlIFNxdWFyZSkgdGhhdCBhcmUgb3V0IG9mIHdhbGtpbmcgcmFuZ2UgYnV0IGNsb3NlIGVub3VnaCBmb3IgYSBiaWtlIHJpZGUuIFRoaXMgaXMgcHVyZWx5IHNwZWN1bGF0aXZlIGJ1dCBtYWtlcyBmb3IgYW4gaW50ZXJlc3RpbmcgaWRlYSB0byBmdXJ0aGVyIGludmVzdGlnYXRlLiBXZSB3aWxsIHRvdWNoIG9uIHRoaXMgYSBiaXQgbW9yZSBpbiB0aGUgbmV4dCBzZWN0aW9uLgoKIyMjIyBEYWlseSB0cmFmZmljCgpXZSBwdXQgdGhlIHRoZW9yeSBvZiB1c2luZyBJbmRlZ28gYXMgYSBmb3JtIG9mIGNvbW11dGUgdG8gdGhlIHRlc3QsIHVzaW5nIGRhaWx5IHRyYWZmaWMgYXMgYSBwcm94eSBmb3IgbWVhc3VyZW1lbnRzLiBXZSBwaWNrZWQgUTIgb2YgMjAxNiwgd2hpY2ggaXMgdGhlIHF1YXJ0ZXIgd2l0aCB0aGUgc2Vjb25kIG1vc3QgYmlrZSByaWRlcy4gV2UgaWRlYWxseSB3b3VsZCBoYXZlIGxpa2VkIHRvIGNob3NlbiBRMyBvZiAyMDE2IChtb3N0IHJlY2VudCBzdW1tZXIgcXVhcnRlciksIHdoaWNoIGlzIHRoZSBtb3N0IHBvcHVsYXIgcXVhcnRlciwgYnV0IHRoaXMgdGltZSBwZXJpb2Qgb3ZlcmxhcHMgd2l0aCBzdW1tZXIgdmFjYXRpb24gZm9yIG1hbnkgc3R1ZGVudHMuIFNpbmNlIHdlIGRpZCBub3Qgd2FudCB0byBpZ25vcmUgdGhpcyBwYXJ0aWN1bGFyIGRlbW9ncmFwaGljIHdoZW4gYW5hbHl6aW5nIHRoZSB1c2Ugb2YgYmlrZSB0cmlwcyBhcyBhIGZvcm0gb2YgY29tbXV0ZSwgd2UgdGh1cyBzZXR0bGVkIG9uIFEyIG9mIDIwMTYuIFdlIGNvbnNpZGVyZWQgYXZlcmFnZSBob3VybHkgdXNlcyBvZiBJbmRlZ28gYmlrZXMgcGVyIGRheSBmb3IgZXZlcnkgZGF5IG9mIFEyIG9mIDIwMTYuIFdlIHRoZW4gcGxvdHRlZCB0aGlzIG9uIGEgMjQtaG91ciB0aW1lLXNjYWxlLCB3aGljaCBpcyBzaG93biBiZWxvdy4gQXMgaWxsdXN0cmF0ZWQgYnkgdGhlIHBsb3QsIHRoZXJlIGFyZSBkcmFzdGljIGRpZmZlcmVuY2VzIGJldHdlZW4gd2Vla2RheSBhbmQgd2Vla2VuZCB1c2FnZSBhY3Jvc3MgYW55IHN0YW5kYXJkIDI0LWhvdXIgd2luZG93LiBPbiB3ZWVrZGF5cywgcGVhayBwZXJpb2RzIG9mIGJpa2UgdHJpcHMgd2VyZSBhcm91bmQgODowMCBBTSBhbmQgNTowMCBQTSwgd2hpY2ggY29ycmVzcG9uZCB0byB0aGUgdGltZSBmcmFtZXMgdGhhdCBtb3N0IGluZGl2aWR1YWxzIGNvbW11dGUgcmVzcGVjdGl2ZWx5IHRvIGFuZCBmcm9tIHdvcmsgYW5kIHNjaG9vbC4gT24gd2Vla2VuZHMsIGhvd2V2ZXIsIHBlYWsgdXNhZ2Ugb2NjdXJlZCBkdXJpbmcgdGhlIGFmdGVybm9vbi4gTW9yZW92ZXIsIG1vcmUgYmlrZSB0cmlwcyB3ZXJlIG9ic2VydmVkIG9uIHdlZWtkYXlzIGZvciBhbG1vc3QgYW55IGdpdmVuIHRpbWUgaW50ZXJ2YWwgdGhhbiB0aGF0IG9mIHdlZWtlbmRzLiBUaHVzLCB0aGlzIG1heSBwb3RlbnRpYWxseSBkZW1vbnN0cmF0ZSB0aGF0IEluZGVnbyBoYXMgYmVjb21lIGEgbWVhbnMgZm9yIGV2ZXJ5ZGF5IGNvbW11dGUgaW4gUGhpbGFkZWxwaGlhLiAKCmBgYHtyfQojIDIuIFBlYWsgUGVyaW9kcyAoZ3JvdXAgYnkgaG91ciwgd2Vla2RheXMvd2Vla2VuZHMsIHNlYXNvbikKIyAyLjEgV2Vla2RheXMvd2Vla2VuZHMgaGlzdG9ncmFtIG9mIHN0YXJ0IChyZW50YWwpIGhvdXJzIGZvciBhbGwgc3RhdGlvbnMgaW4gMjAxNlEyCmxpYnJhcnkobHVicmlkYXRlKQpRMlkxNiA8LSBkZltkZiR5ZWFyPT0yMDE2ICYgZGYkbW9udGggJWluJSBjKCIwNCIsIjA1IiwiMDYiKSwgXQpRMlkxNiRkYXlfdHlwZSA8LSBjaHJvbjo6aXMud2Vla2VuZChRMlkxNiRkYXRlKQpRMlkxNiRkYXlfdHlwZSA8LSBpZmVsc2UoUTJZMTYkZGF5X3R5cGUgPT0gMSwgIldlZWtlbmRzIiwgIldlZWtkYXlzIikKbl93a25kcyA8LSBsZW5ndGgodW5pcXVlKFEyWTE2W1EyWTE2JGRheV90eXBlPT0iV2Vla2VuZHMiLDE0XSkpCm5fd2tkeXMgPC0gbGVuZ3RoKHVuaXF1ZShRMlkxNltRMlkxNiRkYXlfdHlwZT09IldlZWtkYXlzIiwxNF0pKQp3a25kc19wZWFrIDwtIFEyWTE2W1EyWTE2JGRheV90eXBlPT0iV2Vla2VuZHMiLF0gJT4lIGdyb3VwX2J5KHNfaG91cixkYXlfdHlwZSkgJT4lIHN1bW1hcml6ZShyZW50YWxfZnJlcSA9IG4oKS9uX3drbmRzKQp3a2R5c19wZWFrIDwtIFEyWTE2W1EyWTE2JGRheV90eXBlPT0iV2Vla2RheXMiLF0gJT4lIGdyb3VwX2J5KHNfaG91cixkYXlfdHlwZSkgJT4lIHN1bW1hcml6ZShyZW50YWxfZnJlcSA9IG4oKS9uX3drZHlzKQpwZWFrIDwtIHJiaW5kKHdrbmRzX3BlYWssIHdrZHlzX3BlYWspCnJtKHdrbmRzX3BlYWssIHdrZHlzX3BlYWspCnBlYWskc19ob3VyIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHBlYWskc19ob3VyKSkKZm9vMSA8LSBjKDAsNCw4LDEyLDE2LDIwKQpmb28yIDwtIGMoIjEyYW0iLCI0YW0iLCI4YW0iLCIxMnBtIiwiNHBtIiwiOHBtIikKYGBgCgoKYGBge3IgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9M30KZ2dwbG90KHBlYWssIGFlcyhzX2hvdXIsIHJlbnRhbF9mcmVxKSkgKwogIGdlb21fbGluZShhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgbGFicyh0aXRsZT0iUGVhayBSZW50YWwgSG91cnMgMjAxNlEyIiwKICAgICAgIHN1YnRpdGxlPSJBdmVyYWdlIERhaWx5IFJlbnRhbCBGcmVxdWVuY3kgYnkgSG91ciIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoIiIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMSwgbGFiZWxzID0gZm9vMikKYGBgCgpXZSBub3cgZXh0ZW5kIG91ciBhbmFseXNpcyB0byBhbGwgcXVhcnRlcmx5IGRhdGEgYXZhaWxhYmxlLCB3aGljaCBpcyBzaG93biBpbiB0aGUgcGxvdCBpbW1lZGlhdGVseSBiZWxvdy4gTXVjaCBvZiB0aGUgc2FtZSB0cmVuZHMgZnJvbSBRMiBvZiAyMDE2IGNhbiBiZSBnZW5lcmFsaXplZCB0byBvdGhlciBxdWFydGVycy4gQXBhcnQgZnJvbSBhIGZldyBkYXRhIHBvaW50cywgdGhlIG51bWJlciBvZiBiaWtlIHRyaXBzIG9uIHdlZWtkYXlzIGFyZSBhbG1vc3QgYWx3YXlzIGxhcmdlciB0aGFuIHRoYXQgb2Ygd2Vla2VuZHMuIEhvd2V2ZXIsIHdlIGFsc28gb2JzZXJ2ZSB0aGUgYWZvcmVtZW50aW9uZWQgc2Vhc29uYWxpdHkgb2YgYmlrZSB0cmlwcywgaW4gd2hpY2ggbW9yZSBiaWtlIHRyaXBzIGFyZSBvYnNlcnZlZCBpbiB0aGUgc3VtbWVyIHRoYW4gaW4gdGhlIHdpbnRlciBtb250aHMuIFRoaXMgaXMgZXNwZWNpYWxseSB0cnVlIGZvciB0cmlwcyBvbiB3ZWVrZW5kIChlLmcuIHVzYWdlIG9uIHdlZWtlbmRzIGluIFNlcHQgb2YgMjAxNSBpcyBieSBmYXIgdGhlIGxhcmdlc3QpLiBUaGVyZWZvcmUsIHVzZSBvZiBJbmRlZ28gYXMgbGVpc3VyZSBkdXJpbmcgc3VtbWVyIHdlZWtlbmRzIG1heSBiZSB0aGUgY2F1c2FsIHZhcmlhYmxlIG9mIHRoZSBwZWFrcyB3ZSBvYnNlcnZlLiAKCmBgYHtyfQojIDIuMiBNb250aCBhZ2dyZWdhdGlvbiBvZiByZXN1bHRzCnF1YXJ0ZXJfYWdnciA8LSBkZgojcXVhcnRlcl9hZ2dyJHF1YXJ0ZXIgPC0gKGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHF1YXJ0ZXJfYWdnciRtb250aCkpIC0gMSkgJS8lIDMgKyAxCnF1YXJ0ZXJfYWdnciRkYXlfdHlwZSA8LSBjaHJvbjo6aXMud2Vla2VuZChxdWFydGVyX2FnZ3IkZGF0ZSkKcXVhcnRlcl9hZ2dyJGRheV90eXBlIDwtIGlmZWxzZShxdWFydGVyX2FnZ3IkZGF5X3R5cGUgPT0gMSwgIldlZWtlbmRzIiwgIldlZWtkYXlzIikKcXVhcnRlcl9hZ2dyIDwtIHF1YXJ0ZXJfYWdnciAlPiUgc2VsZWN0KHllYXIsIG1vbnRoLCBkYXRlLCBkYXlfdHlwZSkKcXVhcnRlcl9hZ2dyIDwtIHF1YXJ0ZXJfYWdnciAlPiUgZ3JvdXBfYnkoZGF0ZSwgeWVhciwgbW9udGgsIGRheV90eXBlKSAlPiUgc3VtbWFyaXNlKGNvdW50PW4oKSkKcXVhcnRlcl9hZ2dyIDwtIHF1YXJ0ZXJfYWdnciAlPiUgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheV90eXBlKSAlPiUgc3VtbWFyaXNlKGF2ZXJhZ2U9bWVhbihjb3VudCkpCnF1YXJ0ZXJfYWdnciRubyA8LSAoMDoobnJvdyhxdWFydGVyX2FnZ3IpLTEpKSAlLyUgMiArIDEKZm9vMSA8LSBzZXEoMSwyMSxieT0zKQpmb28yIDwtIGMoIkFwciAyMDE1IiwiSnVsIDIwMTUiLCJPY3QgMjAxNSIsICJKYW4gMjAxNiIsICJBcHIgMjAxNiIsIkp1bCAyMDE2IiwiT2N0IDIwMTYiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CmdncGxvdChxdWFydGVyX2FnZ3IsIGFlcyhubywgYXZlcmFnZSkpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuMSwgYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGxhYnModGl0bGU9Ik1vbnRobHkgUmVudGFsIFBhdHRlcm4iLAogICAgICAgc3VidGl0bGU9IkRhaWx5IEF2ZXJhZ2Ugb2YgUmVudGFsIEZyZXF1ZW5jaWVzIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBmb28xLCBsYWJlbHMgPSBmb28yKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSkKYGBgCgoKSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBvbmUgb2YgdGhlIG1vcmUgZ2xhcmluZyBkZWZpY2llbmNpZXMgb2Ygb3VyIHJhdyBkYXRhc2V0IGlzIHRoZSBsYWNrIG9mIGhpc3RvcmljYWwgZGF0YTsgd2Ugb25seSBoYXZlIGRhdGEgZnJvbSBBcHJpbCAyMDE1IG9ud2FyZHMuIEl0IHRodXMgbWFrZXMgaXQgbW9yZSBkaWZmaWN1bHQgdG8gZHJhdyBjb25jbHVzaXZlIHJlbWFrcnMgcmVnYXJkaW5nIHllYXJseSBvciBldmVuIHNlYXNvbmFsIHBhdHRlcm5zIHRoYXQgcmVxdWlyZSBkYXRhIG92ZXIgbG9uZ2VyIHRpbWUgcGVyaW9kcy4gTm9uZXRoZWxlc3MsIHNldmVyYWwgaW1wb3J0YW50IHRyZW5kcyBjYW4gc3RpbGwgYmUgb2JzZXJ2ZWQgb3ZlciB0aGUgdGltZSBwZXJpb2QgZm9yIHdoaWNoIHRoZSBkYXRhc2V0IGlzIGF2YWlsYWJsZS4KCkZvciBpbnN0YW5jZSwgbGV0IHVzIHRha2UgYSBsb29rIGF0IG1vbnRobHkgYmlrZSB0cmlwcyBhcyBkZXRlcm1pbmVkIGZyb20gdGhlIG51bWJlciBvZiBhdmVyYWdlIHRyaXBzIHBlciBkYXksIHNob3duIGJlbG93LiBTdWNoIGFuIGFnZ3JlZ2F0aW9uLCBhcyBvcHBvc2VkIHRvIHRha2luZyB0aGUgbW9udGhseSBhdmVyYWdlIGFzIGlzLCB3YXMgZG9uZSB0byBzbW9vdGggb3V0IGRhaWx5IGZsdWN0dWF0aW9ucyB0aGF0IGNhbiBiZSBjb25zaWRlcmVkIG5vaXNlIGFzIHdlIG1ha2Ugb3VyIHRpbWUgc2NhbGUgbW9yZSBncmFudWxhci4gV2Ugb2JzZXJ2ZSB0d28gbWFpbiB0aGVtZXMuIEZpcnN0LCB0aGVyZSBzZWVtcyB0byBiZSBwYXJ0aWN1bGFyIG1vbnRocyBpbiB3aGljaCBiaWtlIHRyaXBzIGFyZSBtb3JlIGFuZCBsZXNzIGZyZXF1ZW50LiBBY3Jvc3MgYW55IGdpdmVuIGNhbGVuZGFyIHllYXIsIHRoZXJlIGFyZSBtb3JlIGJpa2UgcmlkZXMgdGhyb3VnaCB0aGUgc3VtbWVyLCB1bHRpbWF0ZWx5IHBlYWtpbmcgaW4gU2VwdGVtYmVyLiBCaWtlIHRyaXBzIGRlY2xpbmUgdGhlcmVhZnRlciwgaGl0dGluZyBsb3cgcG9pbnRzIGluIERlY2VtYmVyL0phbnVhcnkuIFRoaXMgaXMgaW50dWl0aXZlIGFzIGdvb2Qgd2VhdGhlciBpcyBjb3JyZWxhdGVkIHdpdGggaG93IHdpbGxpbmcgYW5kIHByYWN0aWNhbCBiaWtlIHJpZGVzIGZ1bmN0aW9uIGFzIGEgbWVhbnMgb2YgdHJhbnNwb3J0YXRpb24uIFRodXMsIHdpdGggYWJ5c21hbCB3ZWF0aGVyIGNvbmRpdGlvbnMgaW4gdGhlIHdpbnRlciwgcGVvcGxlIGFyZSBwcm9iYWJseSBsZXNzIHdpbGxpbmcgdG8gdXNlIEluZGVnbyBiaWtlcy4gU2Vjb25kLCB3ZSBvYnNlcnZlIEluZGVnbydzIHBvcHVsYXJpdHkgaW5jcmVhc2UgdGhyb3VnaCB0aW1lIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgYmlrZSB0cmlwcyBpbiBhIGdpdmVuIG1vbnRoIGFzIGNvbXBhcmVkIHRvIHRoZSBzYW1lIG1vbnRoIGluIGEgcHJlY2VkaW5nIHllYXIuIEluIFNlcHQgMjAxNiwgdXNhZ2Ugd2FzIGF0IGFuIGFsbC10aW1lIGhpZ2gsIHdpdGggbW9yZSB0aGFuIDI2MDAgcmlkZXMgcGVyIGRheSAoY2YuIGFwcHJveGltYXRlbHkgMjI1MCByaWRlcyBpbiBTZXB0IDIwMTYpLiBJbiBEZWMgMjAxNiwgYmlrZSByaWRlcyB3ZXJlIHJvdWdobHkgYSBsaXR0bGUgaGlnaGVyIHRoYW4gMTAwMCByaWRlcyBwZXIgZGF5IChjZi4gNjAwIGluIERlYyAyMDE1KS4gSXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgY29uZm91bmRpbmcgZmFjdG9ycyBzdWNoIGFzIHdlYXRoZXIgY29uZGl0aW9ucyBpbiBhIGdpdmVuIHllYXIgKGUuZy4gRWwgTmlubykgYW5kIHRoZSBpbnN1ZmZpY2llbnQgaGlzdG9yaWNhbCBkYXRhIHdlIGhhdmUgb24taGFuZCBtYXkgc29tZXdoYXQgcmVuZGVyIG91ciByZXN1bHRzIHF1ZXN0aW9uYWJsZS4gV2UsIGhvd2V2ZXIsIGJlbGlldmUgdGhpcyBpcyBub3QgdGhlIGNhc2Ugc2luY2UgbW9yZSBlY29mcmllbmRseSBtZWFucyBvZiB0cmFuc3BvcnRhdGlvbiBzaG91bGQgdGhlb3JldGljYWxseSBiZWNvbWUgbW9yZSBwb3B1bGFyIGR1ZSB0byBncm93aW5nIGVudmlyb25tZW50YWwgc2VudGltZW50cyBpbiB0aGUgbWFzcyBwb3B1bGF0aW9uLgoKYGBge3IgcmVzdWx0cz0iaGlkZSIsZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9M30KIyBNb250aGx5IEJpa2UgVHJpcHMKZGZfY291bnRfYnlfbW9udGggPC0gZGYgJT4lIGdyb3VwX2J5KHllYXIsIG1vbnRoKSAlPiUgc3VtbWFyaXplKGNvdW50ID0gbigpKQpkZl9hdmVyYWdlX2J5X21vbnRoIDwtIGRmICU+JSBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUgc3VtbWFyaXplKGNvdW50ID0gbigpKQpkZl9hdmVyYWdlX2J5X21vbnRoIDwtIGRmX2F2ZXJhZ2VfYnlfbW9udGggJT4lIGdyb3VwX2J5KHllYXIsIG1vbnRoKSAlPiUgc3VtbWFyaXplKGF2ZyA9IG1lYW4oY291bnQpKQpkZl9jb3VudF9ieV9tb250aCRkYXRlIDwtIHpvbzo6YXMueWVhcm1vbihwYXN0ZShkZl9jb3VudF9ieV9tb250aCR5ZWFyLCBkZl9jb3VudF9ieV9tb250aCRtb250aCksICIlWSAlbSIpCmRmX2NvdW50X2J5X21vbnRoJGF2ZXJhZ2UgPC0gZGZfYXZlcmFnZV9ieV9tb250aCRhdmcKZGZfY291bnRfYnlfbW9udGgkbm8gPC0gMTpucm93KGRmX2NvdW50X2J5X21vbnRoKQpkZl9jb3VudF9ieV9tb250aCA8LSBkZl9jb3VudF9ieV9tb250aFssLWMoMSwyKV0Kcm0oZGZfYXZlcmFnZV9ieV9tb250aCkKZGZfY291bnRfYnlfbW9udGggPC0gdGlkeXI6OmdhdGhlcihkZl9jb3VudF9ieV9tb250aCwgdHlwZSwgdmFsdWUsIGMoMSwzKSkKCmZvbzEgPC0gYygwLCA1MDAsIDEwMDAsIDE1MDAsIDIwMDAsIDI1MDAsIDMwMDApCmZvbzIgPC0gYygzLCA2LCA5LCAxMiwgMTUsIDE4LCAyMSkKCmdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKG5vLCB2YWx1ZSwgY29sb3I9ImJsdWUiKSwKICAgICAgICAgICAgZGZfY291bnRfYnlfbW9udGhbZGZfY291bnRfYnlfbW9udGgkdHlwZT09ImF2ZXJhZ2UiLF0pICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjEpICsKICBnZ3RpdGxlKCJNb250aGx5IEluZGVnbyBCaWtlIFRyaXBzXG4oQXZlcmFnZSBwZXIgZGF5KSIpICsKICB4bGFiKCJNb250aCIpICsKICB5bGFiKCJDb3VudCIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoImN5bCIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSxsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMzAwMCksIGJyZWFrcyA9IGZvbzEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMiwgbGFiZWxzID0gZGZfY291bnRfYnlfbW9udGgkZGF0ZVtmb28yXSkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksICJjbSIpKQpgYGAKCkFzIGFuIGludGVyZXN0aW5nIHRhbmdlbnQsIHdlIGV4YW1pbmUgdGhlIG1vc3QgcG9wdWxhciByb3V0ZXMgYmFzZWQgb24gYSBkYWlseSBhdmVyYWdlIG9mIHRyaXBzIGJldHdlZWVuIGFsbCBzdGF0aW9ucyBhY3Jvc3MgYXZhaWxhYmxlIHF1YXJ0ZXJzLiBXZSBzZWUgdGhhdCB0aGUgbW9zdCBwb3B1bGFyIHJvdXRlcyBmcmVxdWVudGVkIGJ5IG1vc3QgdXNlcnMgYXJlIHRob3NlIGJldHdlZW4gaHVicyAoaS5lLiBhbHJlYWR5IHBvcHVsYXIgc3RhdGlvbnMpLiBJbiB0aGUgaW50ZXJhY3RpdmUgbWFwIGJlbG93LCB3ZSBwbG90IHRoZSBFdWNsaWRlYW4gZGlzdGFuY2Ugb2YgdGhlIHRvcCBmaXZlIG1vc3QgcG9wdWxhciByb3V0ZXMuIEZvciBpbnN0YW5jZSwgb25lIHN1Y2ggcm91dGUgaXMgdGhhdCBiZXR3ZWVuIFVuaXZlcnNpdHkgU3RhdGlvbiBhbmQgMjNyZCBhbmQgU291dGggKGkuZS4gZG93bnRvd24pLiBJdCBpcyB3b3J0aCBub3RpbmcgdGhhdCBtb3N0IG9mIHRoZXNlIHJvdXRlcyBhcmUgZWl0aGVyIGFjcm9zcyB0aGUgZG93bnRvd24gYXJlYSBvciBhY3Jvc3MgdGhlIFNjaHV5bGtpbGwgUml2ZXIsIHdoaWNoIHNvbWV3aGF0IHN1YnN0YW50aWF0ZXMgdGhlIGFzc2VydGlvbiB0aGF0IHBvcHVsYXIgcm91dGVzIGFyZSBhIG1lYW5zIGZvciBjb21tdXRlLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMjIyMjIyMjIFBvcHVsYXIgUm91dGVzICMjIyMjIyMjIAojIENvbnRlbnQKIyBNb3N0IFBvcHVsYXIgMjAgUm91dGVzCiMgU2luZ2xlIGRvdHMgbWVhbnMgcmVudCBmcm9tIHRoZXNlIHN0YXRpb25zIGFuZCByZXR1cm4gdG8gdGhlc2Ugc3RhdGlvbnMgYXMgd2VsbApwb3B1bGFyX3JvdXRlIDwtIGRmICU+JSBncm91cF9ieShkYXRlLCB5ZWFyLCBtb250aCwgc3RhcnRfc3RhdGlvbl9pZCwgZW5kX3N0YXRpb25faWQpICU+JSBzdW1tYXJpc2UoY291bnQ9bigpKQpwb3B1bGFyX3JvdXRlIDwtIHBvcHVsYXJfcm91dGUgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQsIGVuZF9zdGF0aW9uX2lkKSAlPiUgc3VtbWFyaXNlKGF2Zz1tZWFuKGNvdW50KSkgJT4lIG5hLm9taXQKcG9wdWxhcl9yb3V0ZSA8LSBwb3B1bGFyX3JvdXRlW29yZGVyKC1wb3B1bGFyX3JvdXRlJGF2ZyksXVsxOjIwLF0KIyBFeHRyYWN0IGFsbCBwb2ludHMKbG9jIDwtIGMocG9wdWxhcl9yb3V0ZSRzdGFydF9zdGF0aW9uX2lkLCBwb3B1bGFyX3JvdXRlJGVuZF9zdGF0aW9uX2lkKSAlPiUgdW5pcXVlICU+JSBhcy5pbnRlZ2VyCnN0YXRpb25fbG9jJGlkIDwtIGFzLm51bWVyaWMoc3RhdGlvbl9sb2MkaWQpCmRvdF9sb2MgPC0gZGF0YS5mcmFtZShpZD1sb2MpICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKCmwxIDwtIGxlYWZsZXQoZG90X2xvYykgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRQcm92aWRlclRpbGVzKCdDYXJ0b0RCLlBvc2l0cm9uJykgJT4lCiAgc2V0Vmlldyhsbmc9LTc1LjE2Mzc2MywgbGF0PTM5Ljk1MjU2OCwgem9vbSA9IDEzKSAlPiUKICBhZGRDaXJjbGVNYXJrZXJzKAogICAgY29sb3IgPSAibmF2eSIsCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjUsIAogICAgcG9wdXAgPSB+bmFtZQogICkKcG9wdSA8LSBwb3B1bGFyX3JvdXRlW3BvcHVsYXJfcm91dGUkc3RhcnRfc3RhdGlvbl9pZCE9cG9wdWxhcl9yb3V0ZSRlbmRfc3RhdGlvbl9pZCxdCmRvdF9sb2MyIDwtIGRvdF9sb2MKY29sbmFtZXMocG9wdSlbMV0gPC0gImlkIgpwb3B1JGlkIDwtIGFzLm51bWVyaWMocG9wdSRpZCkKcG9wdSA8LSBwb3B1ICU+JSBsZWZ0X2pvaW4oZG90X2xvYzIsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKY29sbmFtZXMocG9wdSkgPC0gYygic2lkIiwgImlkIiwgImF2ZyIsICJzX2xhdCIsICJzX2xvbmciLCAibmFtZSIsICJkYXRlIikKZG90X2xvYzIkaWQgPC0gYXMubnVtZXJpYyhkb3RfbG9jMiRpZCkKcG9wdSA8LSBwb3B1ICU+JSBsZWZ0X2pvaW4oZG90X2xvYzIsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKY29sbmFtZXMocG9wdSkgPC0gYygic2lkIiwgImlkIiwgImF2ZyIsICJzX2xhdCIsICJzX2xvbmciLCAic19uYW1lIiwgImRhdGUiLCAiZV9sYXQiLCAiZV9sb25nIiwgImVfbmFtZSIsICJkYXRlX2UiKQpmb3IoaSBpbiAxOm5yb3cocG9wdSkpewogIGwxIDwtIGFkZFBvbHlsaW5lcyhsMSwgbGF0ID0gYXMubnVtZXJpYyhwb3B1W2ksIGMoNCwgOCldKSwgbG5nID0gYXMubnVtZXJpYyhwb3B1W2ksIGMoNSwgOSldKSkKfQpsMQpgYGAKCkJhc2VkIG9uIHRoZSBhYm92ZSBmaW5kaW5ncywgd2UgY2FuIHNheSB3aXRoIGEgY2VydGFpbiBsZXZlbCBvZiBjb25maWRlbmNlIHRoYXQgdGhlIHZvbHVtZSBvZiBiaWtlIHRyaXBzIGlzIGFuIGltcG9ydGFudCBpbmRpY2F0b3Igb2YgSW5kZWdvJ3MgdXRpbGl0eSByYXRlIG92ZXIgZGlmZmVyZW50IGNyb3NzLXNlY3Rpb25zIG9mIHRpbWUuIEhvd2V2ZXIsIGl0IGRvZXMgbGl0dGxlIHRvIHRlbGwgdXMgaG93IChub3QgaG93IG11Y2gpIEluZGVnbyBpcyBiZWluZyB1dGlsaXplZC4gV2l0aCB0aGlzIGluIG1pbmQsIHdlIG1vdGl2YXRlIG91ciBuZXh0IHBvcnRpb24gb2YgYW5hbHlzaXMuCgpTaW5jZSB0aGUgaW5oZXJlbnQgcHVycG9zZSBvZiBiaWtlIHNoYXJlIG5ldHdvcmtzIGFyZSB0byBtZWRpYXRlIHJlbGF0aXZlbHkgc2hvcnQgY29tbXV0ZXMsIHdlIHdhbnQgdG8gZXhhbWluZSB0aGUgbGVuZ3RoIG9mIGVhY2ggdHJpcC4gV2Ugc3VybWlzZSB0aGF0IHNob3J0ZXIgdHJpcHMgKDAtMzAgbWludXRlcykgc2hvdWxkIGJlIHRoZSBpZGVhbCByYW5nZSBzaW5jZSBhbnl0aGluZyBiZXlvbmQgdGhhdCBpcyBzaW1wbHkgZXhlcmNpc2UgYW5kIG90aGVyIHRyYW5zcG9ydGF0aW9uIG1ldGhvZHMgYmVjb21lIGluY3JlYXNpbmdseSBtb3JlIGFwcGVhbGluZy4gV2UgY29uc2VxdWVudGx5IGV4cGxvcmUgZmVhdHVyZXMgcmVsYXRlZCB0byB0aGUgbGVuZ3RoIG9mIGJpa2UgcmlkZXMgbmV4dC4KCiMjIyMgVHJpcCBkdXJhdGlvbnMKCkluIHRoaXMgc2VjdGlvbiwgd2UgdHJ5IHRvIGVsdWNpZGF0ZSB0aGUgbW9yZSBzYWxpZW50IHBhdHRlcm5zIHBlcnRhaW5pbmcgdG8gdHJpcCBkdXJhdGlvbnMuIEZyb20gdGhlIHR3byBoaXN0b2dyYW1zIGJlbG93LCB3aGljaCBpbGx1c3RyYXRlIHRoZSBkdXJhdGlvbnMgb2YgYWxsIHRyaXBzIGluIFEyIGFuZCBRNCBvZiAyMDE2IGFjcm9zcyB3ZWVrZGF5cyBhbmQgd2Vla2VuZHMsIHdlIG9ic2VydmUgdGhhdCBtb3N0IHRyaXBzIHRha2VuIHdlcmUgdW5kZXIgMTUgbWludXRlcy4gVGhlc2UgdHdvIHBhcnRpY3VsYXIgcXVhcnRlcnMgd2VyZSBjaG9zZW4gc2luY2UgdGhleSBhcmUgdGhlIGxhdGVzdCBxdWFydGVycyB0aGF0IGFsc28gb2ZmZXJzIGEgY29udHJhc3QgaW4gc2Vhc29uYWxpdHkuIER1cmluZyB3ZWVrZGF5cyBmb3IgYm90aCBxdWFydGVycywgc2hvcnRlciBjb21tdXRlcyAoPCAxNSBtaW51dGVzKSB3ZXJlIHRoZSBvdmVyd2hlbG1pbmcgbWFqb3JpdHkgb2YgdHJpcCBkdXJhdGlvbnMuIFRoZXkgbWFrZSB1cCBtb3JlIHRoYW4gNzAlIG9mIHRoZSB0b3RhbCBjb3VudC4gVGhlIHNhbWUgaXMgdHJ1ZSBmb3Igd2Vla2VuZHMsIGJ1dCB0byBhIGxlc3NlciBleHRlbnQuIFRoaXMgbWF5IG9uY2UgYWdhaW4gYmUgYW4gaW5kaWNhdG9yIHRoYXQgYmlrZXMgdXNlZCBkdXJpbmcgd2Vla2RheXMgYXJlIGZvciBjb21tdXRlIChlLmcuIHRvIGFuZCBmcm9tIHdvcmsgYW5kIHNjaG9vbCkgc28gdGhleSBhcmUgaW5oZXJlbnRseSBzaG9ydGVyLCB3aGVyZWFzIGJpa2VzIGFyZSB1c2VkIGZvciBtb3JlIHJlY3JlYXRpb25hbCBwdXJwb3NlcyBvbiB3ZWVrZW5kcyBzbyB0aGVpciB0aW1lIG9mIHVzZSBpcyBuYXR1cmFsbHkgbG9uZ2VyLiBUaGUgbGF0dGVyIGlzIGNvcnJvYm9yYXRlZCBieSB0aGUgZmFjdCB0aGF0IGR1cmluZyBzdW1tZXIgd2Vla2VuZHMsIHRoZSBmcmVxdWVuY3kgb2YgbG9uZ2VyIHRyaXBzIG91dHdlaWdoZWQgdGhhdCBvZiBzaG9ydGVyIHRyaXBzLgoKSWYgd2UgaWdub3JlIGFueSBkaWZmZXJlbnRpYXRpb24gb2YgdHJpcCBkdXJhdGlvbnMgYmV0d2VlbiB3ZWVrZGF5cyBhbmQgd2Vla2VuZHMsIHdlIG9ic2VydmUgbXVjaCBvZiB0aGUgc2FtZSB0cmVuZHMuIEZvciBRNCBvZiAyMDE2LCB0aGUgbGF0ZXN0IHF1YXJ0ZXJseSBkYXRhIGF2YWlsYWJsZSB0byB1cywgdGhlIG1vc3QgcHJldmFsZW50IGR1cmF0aW9uIG9mIGJpa2UgcmlkZXMgbGllIGJldHdlZW4gNSBhbmQgMTAgbWludXRlcy4gSW4gZmFjdCwgdGhlIG92ZXJ3aGVsbWluZyBtYWpvcml0eSBvZiB0cmlwcyBhcmUgbGVzcyB0aGFuIDMwIG1pbnV0ZXMuIE9uIHRoZSBjb250cmFyeSwgd2Ugc2VlIHRoYXQgbG9uZyB0cmlwcyBhcmUgYWN0dWFsbHkgb3V0bGllcnMgaW4gb3VyIGRhdGFzZXQuIFRyaXBzIGJldHdlZW4gdHdvIGFuZCBmb3VyIGhvdXJzIG9ubHkgbWFrZSB1cCAwLjklIG9mIGFsbCByaWRlcyBpbiBRNCBvZiAyMDE2LCBhbmQgdHJpcHMgZXhjZWVkaW5nIDQgaG91cnMgbWFrZSB1cCAwLjclIG9mIHRoZSB0b3RhbC4KCmBgYHtyfQojIDEuIEdlbmVyYWwgUGF0dGVybiBWaXN1YWxpemF0aW9uCiMgMS4xIFRyaXAgRHVyYXRpb24gRGlzdHJpYnV0aW9ucyAoZGltZW5zaW9uIG9mIHRpbWUpCiMgV2Vla2RheXMvd2Vla2VuZHMgaW4gMjAxNlEyIGFuZCAyMDE2UTQKbGlicmFyeShnZ3RoZW1lcykKUTJZMTYgPC0gZGZbZGYkeWVhcj09MjAxNiAmIGRmJG1vbnRoICVpbiUgYygiMDQiLCIwNSIsIjA2IiksIF0KUTJZMTYkZGF5X3R5cGUgPC0gY2hyb246OmlzLndlZWtlbmQoUTJZMTYkZGF0ZSkKUTJZMTYkZGF5X3R5cGUgPC0gaWZlbHNlKFEyWTE2JGRheV90eXBlID09IDEsICJXZWVrZW5kcyIsICJXZWVrZGF5cyIpClE0WTE2IDwtIGRmW2RmJHllYXI9PTIwMTYgJiBkZiRtb250aCAlaW4lIGMoIjEwIiwiMTEiLCIxMiIpLCBdClE0WTE2JGRheV90eXBlIDwtIGNocm9uOjppcy53ZWVrZW5kKFE0WTE2JGRhdGUpClE0WTE2JGRheV90eXBlIDwtIGlmZWxzZShRNFkxNiRkYXlfdHlwZSA9PSAxLCAiV2Vla2VuZHMiLCAiV2Vla2RheXMiKQpRMlkxNiRkdXJhdGlvbl9jdXQgPC0gY3V0KGFzLm51bWVyaWMoUTJZMTYkZHVyYXRpb24pLCBjKDAsNSwxMCwxNSwyMCwzMCw2MCwxMjAsMjQwLDE0NDApKQpRNFkxNiRkdXJhdGlvbl9jdXQgPC0gY3V0KGFzLm51bWVyaWMoUTRZMTYkZHVyYXRpb24pLCBjKDAsNSwxMCwxNSwyMCwzMCw2MCwxMjAsMjQwLDE0NDApKQpRMlkxNl9kdXIgPC0gUTJZMTYgJT4lIGdyb3VwX2J5KGRheV90eXBlLCBkdXJhdGlvbl9jdXQsIGRhdGUpICU+JSBzdW1tYXJpc2UoY291bnQ9bigpKQpRMlkxNl9kdXI8LSBRMlkxNl9kdXIgJT4lIGdyb3VwX2J5KGRheV90eXBlLCBkdXJhdGlvbl9jdXQpICU+JSBzdW1tYXJpc2UoYXZlcmFnZT1tZWFuKGNvdW50KSkKUTJZMTZfZHVyJFF1YXJ0ZXIgPC0gIjIwMTZRMiIKUTRZMTZfZHVyIDwtIFE0WTE2ICU+JSBncm91cF9ieShkYXlfdHlwZSwgZHVyYXRpb25fY3V0LCBkYXRlKSAlPiUgc3VtbWFyaXNlKGNvdW50PW4oKSkKUTRZMTZfZHVyPC0gUTRZMTZfZHVyICU+JSBncm91cF9ieShkYXlfdHlwZSwgZHVyYXRpb25fY3V0KSAlPiUgc3VtbWFyaXNlKGF2ZXJhZ2U9bWVhbihjb3VudCkpClE0WTE2X2R1ciRRdWFydGVyIDwtICIyMDE2UTQiCmR1cl9jdXQgPC0gcmJpbmQoUTJZMTZfZHVyLCBRNFkxNl9kdXIpCnJtKFEyWTE2X2R1ciwgUTRZMTZfZHVyLCBRMlkxNiwgUTRZMTYpCmZvbyA8LSBjKCI8NW1pbiIsICI1LTEwbWluIiwgIjEwLTE1bWluIiwgIjE1LTIwbWluIiwgIjIwLTMwbWluIiwgIjMwLTYwbWluIiwgIjEtMmhyIiwgIjItNGhyIiwgIj40aHIiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTN9CmdncGxvdChkdXJfY3V0LCBhZXMoZHVyYXRpb25fY3V0LCBhdmVyYWdlKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsPVF1YXJ0ZXIpLCBzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArCiAgZmFjZXRfZ3JpZCguIH4gZGF5X3R5cGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGU9IkRhaWx5IFRyaXAgRHVyYXRpb24gSGlzdG9ncmFtIiwKICAgICAgIHN1YnRpdGxlPSJGb3IgMjAxNlEyIGFuZCAyMDE2UTQiLAogICAgICAgeGxhYj0iIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiICIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZvbykgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSkpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwNzJCMiIsICIjQ0M3OUE3IikpCmBgYAoKV2UgbmV4dCBleGFtaW5lIHRoZSBtZWRpYW4gcmVudGFsIGR1cmF0aW9uIGZvciBlYWNoIHN0YXRpb24sIHBhcnRpY3VsYXJseSBmb3IgUTIgdG8gUTQgb2YgMjAxNi4gV2UgY2hvc2UgdGhpcyByYW5nZSBzaW5jZSBpdCB3YXMgdGhlIG9ubHkgcmFuZ2Ugb2YgY29udGl1b3VzIHF1YXJ0ZXJseSBkYXRhIHRoYXQgaGFkIGNvbXBsZXRlIHN0YXRpb24gZW50cmllcyBmb3IgdHJpcCBkdXJhdGlvbnMuIEluIHRoZSBpbnRlcmFjdGl2ZSBtYXAgYmVsb3csIGxhcmdlciBjaXJjbGVzIGNvcnJlc3BvbmQgdG8gbGFyZ2VyIHZhbHVlcyB3aXRoIHJlc3BlY3QgdG8gdHJpcCBkdXJhdGlvbnMuIFRoZSBjb2xvciBzY2hlbWUgcmVtYWlucyB0aGUgc2FtZSBhcyBwcmV2aW91c2x5IG1lbnRpb25lZC4gVGhlcmVmb3JlLCBhIGxhcmdlciByZWQgY2lyY2xlIGltcGxpZXMgdGhhdCBhIGJpa2UgdXNlciByZW50ZWQgZnJvbSBhIHBhcnRpY3VsYXIgc3RhdGlvbiBhbmQgcm9kZSB0aGVpciBiaWtlIGZvciBhIGxvbmdlciBkdXJhdGlvbi4gV2Ugc2VlIHRoYXQgbGFyZ2VyIHJlZCBjaXJjbGVzIGNvcnJlc3BvbmQgdG8gc3RhdGlvbnMgaW4gdGhlIG91dHNraXJ0cyBvZiBQaGlsYWRlbHBoaWEuIFRoZXJlZm9yZSwgdGhleSBlaXRoZXIgdXNlZCB0aGUgYmlrZSBmb3IgYSBsb25nZXIgY29tbXV0ZSBvciB0aGV5IHVzZWQgZm9yIGxlaXN1cmVseSBwdXJwb3Nlcy4gRm9yIGluc3RhbmNlLCBvbmUgb2YgdGhlIGxhcmdlciByZWQgY2lyY2xlcyBkZW5vdGVzIFBNT0EsIHdoaWNoIHdlIHByZXZpb3VzbHkgbWVudGlvbmVkIGFzIGEgcG9wdWxhciBzdGF0aW9uIGR1cmluZyB0aGUgc3VtbWVyIGFuZCB3ZWVrZW5kcy4gSG93ZXZlciwgaXQgaXMgaW1wb3NzaWJsZSB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gdGhlIHR3byBiYXNlZCBvbiBvdXIgZGF0YXNldCBhbG9uZS4gSXQgaXMgaG93ZXZlciBpbnRyaWd1aW5nIGluIG5hdHVyZS4KCmBgYHtyfQojIDEuMiBNZWRpYW4gUmVudGFsIER1cmF0aW9uIGZvciBFYWNoIFN0YXRpb24gKDIwMTYgUTItUTQpIChkaW1lbnNpb24gb2Ygc3BhY2UpCiMgY2hvb3NlIDIwMTYgUTItUTQgc2luY2UgYWxsIHN0YXRpb25zIGhhdmUgY29tcGxldGUgZGF0YSBpbiB0aGlzIHBlcmlvZApzdGF0aW9uX2FuYTIgPC0gZGZbZGYkeWVhcj09MjAxNiAmIGRmJG1vbnRoICVpbiUgYygiMDQiLCIwNSIsIjA2IiwiMDciLCIwOCIsIjA5IiwiMTAiLCIxMSIsIjEyIiksIF0KbWVkaWFuX3N0YXQgPC0gc3RhdGlvbl9hbmEyICU+JSBncm91cF9ieShzdGFydF9zdGF0aW9uX2lkKSAlPiUgc3VtbWFyaXNlKG1lZD1tZWRpYW4oZHVyYXRpb24pKQojIFBsb3QgaW4gYSBtYXAsIGRvdCBzaXplID0gbWVkaWFuIHJlbnRhbCB0aW1lCmNvbG5hbWVzKG1lZGlhbl9zdGF0KVsxXSA8LSAiaWQiCnN0YXRpb25fbG9jJGlkIDwtIGFzLm51bWVyaWMoc3RhdGlvbl9sb2MkaWQpCm1lZGlhbl9zdGF0JGlkIDwtIGFzLm51bWVyaWMobWVkaWFuX3N0YXQkaWQpCm1lZGlhbl9zdGF0IDwtIG1lZGlhbl9zdGF0ICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKY29udGVudCA8LSBwYXN0ZSgiPGI+IiwgYXMuY2hhcmFjdGVyKG1lZGlhbl9zdGF0JG5hbWUpLCAiPC9iPjxicj4iLAogICAgICAgICAgICAgICAgICJNZWRpYW4gUmVudGFsIFRpbWU6ICIsIG1lZGlhbl9zdGF0JG1lZCwgIm1pbnV0ZXMiLCAiPGJyPiIpCm1lZGlhbl9zdGF0JGNob3NlbiA8LSBtZWRpYW5fc3RhdCRpZCAlaW4lIGMoIjMwMjAiLCAiMzA1MCIsICIzMTI0IikKbWVkaWFuX3N0YXQkY2hvc2VuIDwtIGlmZWxzZShtZWRpYW5fc3RhdCRjaG9zZW4gPT0gMSwgIkNob3NlbiIsICJVbmNob3NlbiIpCnBhbCA8LSBjb2xvckZhY3RvcihjKCJuYXZ5IiwgInJlZCIpLCBkb21haW4gPSBjKCJDaG9zZW4iLCAiVW5jaG9zZW4iKSkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGVhZmxldChtZWRpYW5fc3RhdCkgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRQcm92aWRlclRpbGVzKCdDYXJ0b0RCLlBvc2l0cm9uJykgJT4lCiAgc2V0Vmlldyhsbmc9LTc1LjE2Mzc2MywgbGF0PTM5Ljk1MjU2OCwgem9vbSA9IDEzKSAlPiUKICBhZGRDaXJjbGVNYXJrZXJzKAogICAgcmFkaXVzID0gfm1lZC8yLAogICAgY29sb3IgPSB+cGFsKGNob3NlbiksCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjUsIAogICAgcG9wdXAgPSB+Y29udGVudAogICkKYGBgCgo8YnI+VGhlIGRvdHMgY29sb3JlZCBpbiBuYXZ5IGFyZSB0aG9zZSB0aGF0IHdlIGNob3NlIHRvIGZ1cnRoZXIgYW5hbHl6ZSwgd2hpY2ggd2UgcHJlc2VudCBoZXJlLiBUaGV5IGNvcnJlc3BvbmQgdG8gdGhlIGZvbGxvd2luZyBzdGF0aW9uczoKCiogMzAyMDogVW5pdmVyc2l0eSBDaXR5IFN0YXRpb24KKiAzMDUwOiA5dGggJiBBcmNoIChkb3dudG93bikKKiAzMTI0OiBSYWNlIFN0cmVldCBQaWVyIChhd2F5IGZyb20gdW5pdmVyc2l0eSBhbmQgZG93bnRvd24pCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIDIuIFN0YXRpb24tbGV2ZWwgcmVudGFsIHRpbWUKCiMgU2VsZWN0IHN0YXRpb25zCiMgVGhyZWUgbG9jYXRpb25zIHRoYXQgY291bGQgaGF2ZSB0b3RhbGx5IGRpZmZlcmVudCB0cmF2ZWwgcGF0dGVybnM6CiMgMzAyMDogVW5pdmVyc2l0eSBDaXR5IFN0YXRpb24sIGdsb2JhbCBhdmcgZHVyYXRpb24gMTcuOAojIDMwNTA6IDl0aCAmIEFyY2gsIChkb3dudG93biksIGdsb2JhbCBhdmcgZHVyYXRpb24gMjIuNAojIDMxMjQ6IFJhY2UgU3RyZWV0IFBpZXIsIChhd2F5IGZyb20gdW5pdmVyc2l0eSBhbmQgZG93bnRvd24pLCBnbG9iYWwgYXZnIGR1cmF0aW9uIDQzLjQKCiMgMi4xIE92ZXIgaG91ciBvZiBhIGRheQpzdGF0aW9uX2FuYSA8LSBkZltkZiR5ZWFyPT0yMDE2ICYgZGYkbW9udGggJWluJSBjKCIwNCIsIjA1IiwiMDYiLCIwNyIsIjA4IiwiMDkiLCIxMCIsIjExIiwiMTIiKSwgXQpmb28gPC0gc3RhdGlvbl9hbmEgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQpICU+JSBzdW1tYXJpc2UoYXZlcmFnZT1tZWFuKGR1cmF0aW9uKSkgIyBzZWxlY3Qgc3RhdHRpb25zCnN0YXRpb25fYW5hIDwtIHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJGR1cmF0aW9uPDI0MCxdICMgISEgVHJ1bmNhdGUgZHVyYXRpb24gYXQgNCBocnMgdG8gcmVtb3ZlIG91dGxpZXJzCnN0YXRpb25fYW5hIDwtIHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJHN0YXJ0X3N0YXRpb25faWQgJWluJSBjKCIzMDIwIiwgIjMwNTAiLCAiMzEyNCIpLF0Kc3RhdGlvbl9hbmEkZGF5X3R5cGUgPC0gY2hyb246OmlzLndlZWtlbmQoc3RhdGlvbl9hbmEkZGF0ZSkKc3RhdGlvbl9hbmEkZGF5X3R5cGUgPC0gaWZlbHNlKHN0YXRpb25fYW5hJGRheV90eXBlID09IDEsICJXZWVrZW5kcyIsICJXZWVrZGF5cyIpCmZvbyA8LSBzdGF0aW9uX2FuYSAlPiUgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbl9pZCkgJT4lIHN1bW1hcmlzZShjb3VudD1uKCkpICMgY2hlY2sgdG90YWwgdHJpcHMKc3RhdGlvbl9hbmEgPC0gc3RhdGlvbl9hbmEgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQsIGRheV90eXBlLCBzX2hvdXIpICU+JSBzdW1tYXJpc2UoYXZnX2R1cj1tZWFuKGR1cmF0aW9uKSkKZm9vIDwtIGRhdGEuZnJhbWUoc3RhcnRfc3RhdGlvbl9pZCA9IHJlcChjKCIzMDIwIiwgIjMwNTAiLCAiMzEyNCIpLGVhY2g9NDgpLAogICAgICAgICAgICAgICAgICBkYXlfdHlwZSA9IHJlcChyZXAoYygiV2Vla2RheXMiLCAiV2Vla2VuZHMiKSxlYWNoPTI0KSwzKSwKICAgICAgICAgICAgICAgICAgc19ob3VyID0gcmVwKDA6MjMsNikpCnN0YXRpb25fYW5hJHNfaG91ciA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzdGF0aW9uX2FuYSRzX2hvdXIpKQpzdGF0aW9uX2FuYSRzdGFydF9zdGF0aW9uX2lkIDwtIGFzLmNoYXJhY3RlcihzdGF0aW9uX2FuYSRzdGFydF9zdGF0aW9uX2lkKQpzdGF0aW9uX2FuYSRkYXlfdHlwZSA8LSBhcy5jaGFyYWN0ZXIoc3RhdGlvbl9hbmEkZGF5X3R5cGUpCnN0YXRpb25fYW5hIDwtIHN0YXRpb25fYW5hICU+JSByaWdodF9qb2luKGZvbywgYnk9Yygic3RhcnRfc3RhdGlvbl9pZCI9InN0YXJ0X3N0YXRpb25faWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRheV90eXBlIj0iZGF5X3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNfaG91ciI9InNfaG91ciIpKQpzdGF0aW9uX2FuYVtpcy5uYShzdGF0aW9uX2FuYSldIDwtIDAKZm9vMSA8LSBjKDAsNCw4LDEyLDE2LDIwKQpmb28yIDwtIGMoIjEyYW0iLCI0YW0iLCI4YW0iLCIxMnBtIiwiNHBtIiwiOHBtIikKZ2dwbG90KHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJHN0YXJ0X3N0YXRpb25faWQ9PSIzMDIwIixdLCBhZXMoc19ob3VyLCBhdmdfZHVyKSkgKwogIGdlb21fbGluZShhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgbGFicyh0aXRsZT0iQXZlcmFnZSBUcmlwIER1cmF0aW9uIDIwMTYgUTItUTQiLAogICAgICAgc3VidGl0bGU9IkF0ICgzMDIwKSBVbml2ZXJzaXR5IENpdHkgU3RhdGlvbiwgVG90YWwgVHJpcHM6IDExOTIwIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBmb28xLCBsYWJlbHMgPSBmb28yKQoKZ2dwbG90KHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJHN0YXJ0X3N0YXRpb25faWQ9PSIzMDUwIixdLCBhZXMoc19ob3VyLCBhdmdfZHVyKSkgKwogIGdlb21fbGluZShhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgbGFicyh0aXRsZT0iQXZlcmFnZSBUcmlwIER1cmF0aW9uIDIwMTYgUTItUTQiLAogICAgICAgc3VidGl0bGU9IkF0ICgzMDUwKSA5dGggJiBBcmNoLCBUb3RhbCBUcmlwczogNjEyMSIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoIiIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMSwgbGFiZWxzID0gZm9vMikKCmdncGxvdChzdGF0aW9uX2FuYVtzdGF0aW9uX2FuYSRzdGFydF9zdGF0aW9uX2lkPT0iMzEyNCIsXSwgYWVzKHNfaG91ciwgYXZnX2R1cikpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuMSwgYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGxhYnModGl0bGU9IkF2ZXJhZ2UgVHJpcCBEdXJhdGlvbiAyMDE2IFEyLVE0IiwKICAgICAgIHN1YnRpdGxlPSJBdCAoMzEyNCkgUmFjZSBTdHJlZXQgUGllciwgVG90YWwgVHJpcHM6IDM3MjkiKSArCiAgc2NhbGVfY29sb3JfZml2ZXRoaXJ0eWVpZ2h0KCIiKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksICJjbSIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGZvbzEsIGxhYmVscyA9IGZvbzIpCmBgYAoKCkluIHRoZSBmaXJzdCBwbG90IChVbml2ZXJzaXR5IENpdHkgU3RhdGlvbiksIHdlIGNvbnNkaWVyIHRoZSBzdGF0aW9uIGNsb3NlIHRvIGEgdW5pdmVyc2l0eS4gV2Ugc2VlIHRoYXQgb24gYXZlcmFnZSwgbG9uZ2VyIHRyaXBzIG9jY3VyIG9uIHRoZSB3ZWVrZW5kLiBJbiBmYWN0LCBub25lIG9mIHRoZSB0cmlwcyBvbiB3ZWVrZGF5cyBleGNlZWQgMjUgbWludXRlcy4gV2UgYWxzbyBvYnNlcnZlIHRoYXQgaW4gYm90aCBjYXNlcywgbG9uZ2VyIHRyaXBzIHRlbmQgdG8gb2NjdXIgZHVyaW5nIHRoZSBkYXksIHdoZXJlYXMgc2hvcnRlciB0cmlwcyBvY2N1ciBpbiB0aGUgZWFybHkgbW9ybmluZyBhbmQgdG93YXJkcyBuaWdodCB0aW1lLiBJbiB0aGUgc2Vjb25kIHBsb3QgZGVsaW5lYXRpbmcgdGhlIGRvd250b3duIHN0YXRpb24gKDl0aCAmIEFyY2gpLCB3ZSBzZWUgbXVjaCBvZiB0aGUgc2FtZSB0cmVuZCBhcyB0aGF0IG9mIHRoZSBwcmV2aW91cyBjYXNlLiBIb3dldmVyLCB0aGUgZGlzY3JlcGFuY3kgYmV0d2VlbiB0cmlwIGR1cmF0aW9ucyBhY3Jvc3MgYSAyNC1ob3VyIHdpbmRvdyBvbiB0aGUgd2Vla2VuZHMgYW5kIHRoZSB3ZWVrZGF5cyBpcyBtdWNoIG1vcmUgcHJvbm91bmNlZC4gSW4gb3VyIGxhc3QgcGxvdCBhYm92ZSAoUmFjZSBTdHJlZXQgUGllciksIHdlIGNvbnNpZGVyIGEgc3RhdGlvbiBmYXIgZnJvbSBhIHVuaXZlcnNpdHkgb3IgZG93bnRvd24sIHdoaWNoIHdlIGNhbiBjb25zaWRlciB0byBiZSAibW9yZSBydXJhbC4iIEhlcmUsIHRyaXBzIG9uIHdlZWtkYXlzIGFuZCB3ZWVrZW5kcyBtaXJyb3Igb25lIGFub3RoZXIsIHNob3dpbmcgdGhleSBhcmUgY2xvc2VseSByZWxhdGVkLiBNb3Jlb3ZlciwgdHJpcHMgZnJvbSB0aGlzIG1vcmUgcnVyYWwgc3RhdGlvbiBhcmUgYWxsIG11Y2ggbG9uZ2VyLCBtb3N0IGV4Y2VlZGluZyAyMCBtaW51dGVzIGlycmVzcGVjdGl2ZSBvZiB0aW1lIGludGVydmFscyBkdXJpbmcgdGhlIHdlZWsuIFRoZXJlZm9yZSwgaXQgd291bGQgc2VlbSB0aGF0IGRpZmZlcmVudCBzdGF0aW9ucyBiYXNlZCBvbiB0aGVpciBnZW9ncmFwaGljYWwgbG9jYXRpb24gc2VydmUgYSBkaWZmZXJlbnQgcHVycG9zZSBmb3IgYmlrZSByaWRlcnMuIFN0YXRpb25zIGluIG1vcmUgZGVuc2VseSBwb3B1bGF0ZWQgYXJlYXMgdGVuZCB0byBmYXZvciBzaG9ydGVyIHJpZGVzLCB3aGVyZWFzIGxvbmdlciByaWRlcyBhcmUgbW9yZSBwcmV2YWxlbnQgaW4gbW9yZSByZW1vdGUgbG9jYXRpb25zLiBJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZSBtYWduaXR1ZGUgb2YgbnVtYmVyIG9mIHRyaXBzIG1heSBpbnRyb2R1Y2Ugc29tZSBzb3J0IG9mIGNvdW5mb3VuZGVyIG9uIG91ciBpbnNpZ2h0cyAoZS5nLiBkYXRhIGltYmFsYW5jZSksIGJ1dCB3ZSBpZ25vcmUgdGhpcyBoZXJlLCBzaW5jZSBpdCBpcyBkaWZmaWN1bHQgdG8gYWRkcmVzcyB0aGlzIGlzc3VlIGdpdmVuIHRoZSBsaW1pdGVkIGhpc3RvcmljYWwgZGF0YSB3ZSBoYXZlLiBUaGlzIGlzIGEgcHJvYmxlbSB0aGF0IHdvdWxkIGJlIHNvbHZlZCBuYXR1cmFsbHkgYXMgbW9yZSBkYXRhIGJlY29tZXMgYXZhaWxhYmxlLiAKCmBgYHtyIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CiMgMi4yIE92ZXIgbW9udGggb2YgYSB5ZWFyCnN0YXRpb25fYW5hMiA8LSBkZltkZiR5ZWFyPT0yMDE2ICYgZGYkbW9udGggJWluJSBjKCIwNCIsIjA1IiwiMDYiLCIwNyIsIjA4IiwiMDkiLCIxMCIsIjExIiwiMTIiKSwgXQpzdGF0aW9uX2FuYTIgPC0gc3RhdGlvbl9hbmEyW3N0YXRpb25fYW5hMiRzdGFydF9zdGF0aW9uX2lkICVpbiUgYygiMzAyMCIsICIzMDUwIiwgIjMxMjQiKSxdCnN0YXRpb25fYW5hMiA8LSBzdGF0aW9uX2FuYTJbc3RhdGlvbl9hbmEyJGR1cmF0aW9uPDI0MCxdICMgISEgVHJ1bmNhdGUgZHVyYXRpb24gYXQgNCBocnMgdG8gcmVtb3ZlIG91dGxpZXJzCnN0YXRpb25fYW5hMiA8LSBzdGF0aW9uX2FuYTIgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQsIG1vbnRoKSAlPiUgc3VtbWFyaXNlKG1lZD1tZWRpYW4oZHVyYXRpb24pKQpjb2xuYW1lcyhzdGF0aW9uX2FuYTIpWzFdIDwtICJpZCIKc3RhdGlvbl9hbmEyJGlkIDwtIGFzLm51bWVyaWMoc3RhdGlvbl9hbmEyJGlkKQpzdGF0aW9uX2FuYTIgPC0gc3RhdGlvbl9hbmEyICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkKc3RhdGlvbl9hbmEyJG1vbnRoIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHN0YXRpb25fYW5hMiRtb250aCkpCmZvbzEgPC0gNDoxMgpmb28yIDwtIGMoIkFwciIsIk1heSIsIkp1biIsIkp1bCIsIkF1ZyIsIlNlcHQiLCJPY3QiLCJOb3YiLCJEZWMiKQpnZ3Bsb3Qoc3RhdGlvbl9hbmEyLCBhZXMobW9udGgsIG1lZCkpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPW5hbWUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9bmFtZSkpICsKICBsYWJzKHRpdGxlPSJNZWRpYW4gb2YgVHJpcCBEdXJhdGlvbiIsCiAgICAgICBzdWJ0aXRsZT0iT3ZlciBNb250aHMgKDIwMTYgUTItUTQpIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBmb28xLCBsYWJlbHMgPSBmb28yKQpgYGAKCldlIHJlYWxpemUgaXQgbWF5IGJlIGRpZmZpY3VsdCB0byBleGFtaW5lIHRoZSB0cmVuZHMgb2YgYWxsIHRocmVlIHBsb3RzIGNvbmN1cnJlbnRseSBzbyB3ZSB0aHVzIHBsb3QgdGhlIG1lZGlhbiB0cmlwIGR1cmF0aW9uIG9mIGFsbCB0aHJlZSBzdGF0aW9ucyBpbiBvbmUgc2luZ2xlIHBsb3QsIHdoaWNoIGlzIHNob3duIGFib3ZlLiBJdCBpcyBpbnRlcmVzdGluZyB0byBub3RlIHRoYXQgb3ZlciB0aW1lLCBzaG9ydGVyIHJpZGVzIGJlY29tZSB0aGUgcHJldmFsZW50IHRoZW1lLiBUaGlzIGlzIHRydWUgZm9yIGFsbCB0aHJlZSBzdGF0aW9ucy4gSG93ZXZlciwgdGhlIG1vcmUgcnVyYWwgc3RhdGlvbiBpcyBzdGlsbCBjaGFyYWN0ZXJpemVkIGJ5IGxvbmdlciB0cmlwcyByZWxhdGl2ZSB0byB0aGUgb3RoZXIgdHdvIHN0YXRpb25zIHRoYXQgYXJlIGNsb3NlIHRvIGEgdW5pdmVyc2l0eSBhbmQgY2l0eSBjZW50ZXIuCgpUaHVzIGZhciwgd2UgaGF2ZSBleGFtaW5lZCBob3cgYW5kIGhvdyBtdWNoIEluZGlnbyBpcyB1dGlsaXplZC4gSG93ZXZlciwgd2Ugc3RpbGwgZG8gbm90IGtub3cgaG93IHVzZXJzIHNlbGVjdCB0byB1dGlsaXplIEluZGVnby4gRm9yIGluc3RhbmNlLCB3aGljaCBtZXRob2RzIG9mIHBheW1lbnQgYXJlIG1vcmUgY29tbW9uPyBJcyB0aGVyZSBhIGNoYW5nZSBpbiBwcmVmZXJlbmNlIG92ZXIgdGltZT8gV2Ugc2VlayB0byBhbnN3ZXIgdGhlc2UgcXVlc3Rpb25zIGluIHRoZSBuZXh0IGFuZCBmaW5hbCBzZWN0aW9uIG9mIG91ciBhbmFseXNpcy4KCiMjIyMgVXNlciBwcm9maWxlcwoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBleGFtaW5lIHRoZSBhdHRyaWJ1dGVzIHJlbGF0ZWQgdG8gSW5kZWdvIGJpa2UgdXNlcnMuIFNpbmNlIHRoZSBkYXRhc2V0IHByb3ZpZGVzIG9ubHkgdHdvIGRpbWVuc2lvbnMgcmVnYXJkaW5nIHRoaXMgYXNwZWN0LCB3ZSB1c2UgdGhlIGNvbnZlbnRpb25hbCBtb3NhaWMgcGxvdCB0byBleGFtaW5lIHRoZSBwcm9wb3J0aW9ucyBvZiBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIEluZGVnbyB1c2Vycy4gVGhlc2UgdHdvIGZlYXR1cmVzIGFyZSBwYXNzaG9sZGVyIHR5cGVzIChJbmRlZ28zMCwgSW5kZWdvRmxleCwgYW5kIHdhbGt1cCkgYW5kIHR5cGUgb2YgdHJpcHMgKG9uZS13YXksIHJvdW5kdHJpcCkuCgpIZXJlIGlzIHNvbWUgbW9yZSByZWxldmFudCBpbmZvcm1hdGlvbiByZWdhcmRpbmcgdGhlIGRpZmZlcmVudCBbcGFzc2hvbGRlciB0eXBlc10oaHR0cHM6Ly93d3cucmlkZWluZGVnby5jb20vcGFzc2VzLyk6CgoqIEluZGVnbzMwOiAkMTUvbW9udGgsIHVubGltaXRlZCBvbmUgaG91ciB0cmlwcwoqIEluZGVnb0ZsZXg6ICQxMC95ZWFyIGJhc2UgZmVlLCBhbmQgJDQvaG91cgoqIFdhbGstdXA6ICQ4L2hvdXIKCmBgYHtyfQojIDEuMSBNb3NhaWMgcGxvdCBvZiBvdmVyYWxsIGRhdGEKYWxsX2RhdGFfbW9zYWljIDwtIGRmICU+JSBncm91cF9ieShwYXNzaG9sZGVyX3R5cGUsIHRyaXBfcm91dGVfY2F0ZWdvcnkpICU+JSBzdW1tYXJpemUoY291bnQgPSBuKCkpCmNvbG5hbWVzKGFsbF9kYXRhX21vc2FpYykgPC0gYygiUGFzcyIsICJUcmlwIiwgImNvdW50IikKYGBgCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zfQpsaWJyYXJ5KHZjZCkKdmNkOjptb3NhaWMoeHRhYnMoY291bnQgfiBUcmlwICsgUGFzcywgZGF0YSA9IGFsbF9kYXRhX21vc2FpYyksCiAgICAgICAgICAgIGRpcmVjdGlvbiA9IGMoInYiLCAiaCIpLAogICAgICAgICAgICBncCA9IGdwYXIoZmlsbCA9IGMoImxpZ2h0IGdyZWVuIiwgImxpZ2h0IGJsdWUiLCAibGlnaHQgeWVsbG93IikpLAogICAgICAgICAgICBzcGFjaW5nID0gc3BhY2luZ19oaWdobGlnaHRpbmcsCiAgICAgICAgICAgIGxhYmVsaW5nID0gbGFiZWxpbmdfYm9yZGVyKHJvdF9sYWJlbHMgPSBjKDAsOTAsMCwwKSkpCmBgYAoKRm9yIGFsbCBhdmFpbGFibGUgcXVhcnRlcnMsIHdlIGNhbiBzZWUgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdXNlcnMgZHJhc3RpY2FsbHkgZmF2b3Igb25lLXdheSBvdmVyIHJvdW5kIHRyaXBzLCBhbmQgSW5kZWdvMzAgcGFzc2VzIG92ZXIgd2Fsa3VwIHBheW1lbnQgcGxhbnMuIEluZGVnb0ZsZXggaXMgdGhlIGxlYXN0IHBvcHVsYXIgb3B0aW9uIHJlZ2FyZGxlc3Mgb2Ygcm91dGUgdHlwZS4gSW4gZmFjdCwgdGhlIGNvbWJpbmF0aW9uIG9mIG9uZS13YXkgdHJpcHMgd2l0aCBJbmRlZ28zMCBpcyBvdmVyd2hlbG1pbmcgY2hhcmFjdGVyaXN0aWMsIGFjY291bnRpbmcgZm9yID41MCUgb2YgYWxsIHVzZXJzLCBmb2xsb3dlZCBieSBvbmUtd2F5IHdpdGggd2Fsay11cHMuIEhvd2V2ZXIsIHRoaXMgaXMgYSBleHRyZW1lbHkgcm91Z2ggcmVwcmVzZW50YXRpb24gb2YgdXNlciBwcm9maWxlcyBzaW5jZSB3ZSBpZ25vcmUgaG93IHVzZXIgcHJvZmlsZXMgdmFyeSBvdmVyIHRpbWUuIFdlIGV4YW1pbmUgdGhpcyBuZXh0LgoKTGV0IHVzIGV4YW1pbmUgdGhlIG1vc3QgcG9wdWxhciBvcHRpb24gKEluZGVnbzMwIHggb25lLXdheSksIGFzIHNob3duIGJlbG93LiBXZSBzZWUgdGhhdCB0aGlzIG9wdGlvbiBoYXMgYmVjb21lIG1vcmUgcG9wdWxhciBhdCBhIHN0ZWFkeSByYXRlLCBpbiB3aGljaCBtb3JlIHRoYW4gNzUlIG9mIHVzZXJzIGNob29zZSB0aGlzIG9wdGlvbiBpbiB0aGUgbGFzdGVzdCBxdWFydGVyLgoKYGBge3J9CiMgMS4yIFR5cGUgb2YgcGFzc2hvbGRlcnMsIGZyZXF1ZW5jeSBvZiByb3VuZC10cmlwcyAoZm9yIG1vbnRoKQojIGZvciBtb3N0IHBvcHVsYXIgdHlwZSBvZiB0cmlwIHJvdXRlIGFuZCBwYXNzIHR5cGUKCiMgMi4gVGVuZGVuY3kgb2YgTW92aW5nIHRvIE1vc3QgUG9wdWxhciBDYXRlZ29yeQojIFBsb3Qgb2YgdGhlIHBlcmNlbnRhZ2Ugb2YgKHBhc3Nob2xkZXJfdHlwZSwgdHJpcF9yb3V0ZV9jYXRlZ29yeSkgb3ZlciBxdWFydGVycwojIEZvciBtb3N0IHBvcHVsYXIgY2F0ZWdvcnkKcGVyY19jYXRlZ29yeSA8LSBkZiAlPiUgZ3JvdXBfYnkocGFzc2hvbGRlcl90eXBlLCB0cmlwX3JvdXRlX2NhdGVnb3J5LCB5ZWFyLCBtb250aCkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkKcGVyY19jYXRlZ29yeSRxdWFydGVyIDwtIChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihwZXJjX2NhdGVnb3J5JG1vbnRoKSkgLSAxKSAlLyUgMyArIDEKcGVyY19jYXRlZ29yeSA8LSBwZXJjX2NhdGVnb3J5ICU+JSBncm91cF9ieSh5ZWFyLCBxdWFydGVyLCBwYXNzaG9sZGVyX3R5cGUsIHRyaXBfcm91dGVfY2F0ZWdvcnkpICU+JSBzdW1tYXJpc2UoY291bnRfcXQ9c3VtKGNvdW50KSkgCnBlcmNfY2F0ZWdvcnkkdHlwZSA8LSBwYXN0ZShwZXJjX2NhdGVnb3J5JHRyaXBfcm91dGVfY2F0ZWdvcnksICJ4IiwgcGVyY19jYXRlZ29yeSRwYXNzaG9sZGVyX3R5cGUpCnBlcmNfY2F0ZWdvcnkgPC0gcGVyY19jYXRlZ29yeSAlPiUgZ3JvdXBfYnkoeWVhciwgcXVhcnRlcikgJT4lIG11dGF0ZSh0b3RhbD1zdW0oY291bnRfcXQpKQpwZXJjX2NhdGVnb3J5JHBlcmMgPC0gcm91bmQoMTAwKihwZXJjX2NhdGVnb3J5JGNvdW50X3F0IC8gcGVyY19jYXRlZ29yeSR0b3RhbCksMikKcGVyY19jYXRlZ29yeSA8LSBwZXJjX2NhdGVnb3J5WywtYygzOjUsNyldCnBlcmNfY2F0ZWdvcnkkbm8gPC0gcmVwKDE6NyxlYWNoPTYpCmZvbzEgPC0gMTo3CmZvbzIgPC0gYygiMjAxNVEyIiwiMjAxNVEzIiwiMjAxNVE0IiwgIjIwMTZRMSIsICIyMDE2UTIiLCIyMDE2UTMiLCIyMDE2UTQiKQpmb28zIDwtIGMoIk9uZSBXYXkgeCBJbmRlZ28zMCIpCnRtcDEgPC0gcGVyY19jYXRlZ29yeVtwZXJjX2NhdGVnb3J5JHR5cGUgJWluJSBmb28zLF0KYGBgCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zfQpnZ3Bsb3QodG1wMSwgYWVzKG5vLCBwZXJjKSkgKwogIGdlb21fbGluZShhZXMoY29sb3VyPXR5cGUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXR5cGUpLHNpemUgPSAxLjEpICsKICBsYWJzKHRpdGxlPSJQZXJjZW50YWdlIENoYW5nZSBPdmVyIFF1YXJ0ZXJzIiwKICAgICAgIHN1YnRpdGxlPSJGb3IgTW9zdCBQb3B1bGFyIENhdGVnb3J5XG5PbmUgV2F5IHggSW5kZWdvMzAiKSArCiAgc2NhbGVfY29sb3JfZml2ZXRoaXJ0eWVpZ2h0KCIiKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksICJjbSIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGZvbzEsIGxhYmVscyA9IGZvbzIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxMDApKQpgYGAKCldlIHNlZSBmcm9tIHRoZSBwcmV2aW91cyBtb3NhaWMgcGxvdCB0aGF0IHJvdW5kIHRyaXBzIGFyZSBub3QgcG9wdWxhciBvbiBhdmVyYWdlLiBXZSB3YW50IHRvIHRlc3Qgd2hldGhlciB0aGVyZSBkb2VzIGluZGVlZCBleGlzdCBhIGRlY2xpbmUgaW4gcG9wdWxhcml0eSBvdmVyIHRpbWUuIFdlIHNlZSBmcm9tIHRoZSBwZXJjZW50YWdlIGNoYW5nZSBvdmVyIHF1YXJ0ZXJzIHNob3duIGJlbG93IHRoYXQgcmVnYXJkbGVzcyBvZiBwYXNzIHR5cGVzLCByb3VuZCB0cmlwcyBtYWtlIHVwIGFuIGluY3JlYXNpbmdseSBzbWFsbGVyIGZyYWN0aW9uIG9mIHRoZSB1c2VyIGRlbW9ncmFwaGljLiBXZSBhbHNvIHBvc3R1bGF0ZSB0aGF0IHRoZSBhbHJlYWR5IHVucG9wdWxhciBjb21iaW5hdGlvbiBvZiByb3VuZCB0cmlwcyBhbmQgSW5kZWdvIEZsZXggd291bGQgbWFrZSB1cCBhbiBpbmNyZWFzaW5nbHkgc21hbGxlciBwcm9wb3J0aW9uIGFzIHdlbGwgc2luY2UgdGhleSBhcmUgbW9yZSBmcmVxdWVudCB0cmF2ZWxsZXJzIHRoYXQgd291bGQgZ3JhZHVhbGx5IG1vdmUgdG93YXJkcyBjaGVhcGVyIHBsYW5zLiBUaGlzIGhvd2V2ZXIgaXMgc3BlY3VsYXRpdmUgYW5kIGlzIGNvbnRpbmdlbnQgb24gYSByb2J1c3QsIGxhcmdlIGVub3VnaCBsb25naXR1ZGluYWwgZGF0YXNldCwgd2hpY2ggd2UgZG8gbm90IGhhdmUuIE5vbnRoZWxlc3MsIHRoaXMgcHJvYmxlbSwgYWxiZWl0IGludGVyZXN0aW5nLCBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIG91ciBhbmFseXNpcy4KCmBgYHtyfQojIEZvciByb3VuZHRyaXBzCmZvbzQgPC0gYygiUm91bmQgVHJpcCB4IEluZGVnb0ZsZXgiLCAiUm91bmQgVHJpcCB4IEluZGVnbzMwIiwgIlJvdW5kIFRyaXAgeCBXYWxrLXVwIikKdG1wMiA8LSBwZXJjX2NhdGVnb3J5W3BlcmNfY2F0ZWdvcnkkdHlwZSAlaW4lIGZvbzQsXQpgYGAKCmBgYHtyIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CmdncGxvdCh0bXAyLCBhZXMobm8sIHBlcmMpKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksc2l6ZSA9IDEuMSkgKwogIGxhYnModGl0bGU9IlBlcmNlbnRhZ2UgQ2hhbmdlIE92ZXIgUXVhcnRlcnMiLAogICAgICAgc3VidGl0bGU9Ik92ZXIgQ2F0ZWdvcmllcyIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoIiIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMSwgbGFiZWxzID0gZm9vMikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDcuMTUpKQoKYGBgCgpCYXNlZCBvbiB0aGUgYWJvdmUgYW5hbHlzaXMgb24gdXNlciBwcm9maWxlcywgd2Ugc2VlIHRoYXQgYXMgYmlrZSBzaGFyaW5nIGJlY29tZXMgbW9yZSBwb3B1bGFyIG92ZXIgdGltZSBpbiBQaGlsYWRlbHBoaWEsIG1vcmUgdXNlcnMgb3B0IGZvciBtb3JlIGNvc3QtZWZmaWNpZW50IHBheW1lbnQgcGxhbnMsIHdoaWNoIGlzIGJ5IGZhciBJbmRlZ28zMC4gVGhlIHJpc2Ugb2Ygb25lLXdheSB0cmlwcyBhbHNvIGNvbmplY3R1cmFsbHkgZGVtb25zdHJhdGUgdGhhdCB1c2VycyBhcmUgdXNpbmcgSW5kZWdvIG1vcmUgZnJlcXVlbnRseSBhcyBhIG1lYW5zIG9mIHRyYW5zcG9ydGF0aW9uIG92ZXIgc2hvcnQgZGlzdGFuY2VzIG9yIHRpbWUgcGVyaW9kcy4gT2J2aW91c2x5LCB3ZSBpZ25vcmUgbWFueSBpbXBvcnRhbnQgZmFjdG9ycyByZWxhdGVkIHRvIHVzZXIgcHJvZmlsZXMuIEZvciBpbnN0YW5jZSwgaXMgdGhlcmUgYSBwcmVmZXJlbmNlIGZvciB0aG9zZSByZW50aW5nIGZyb20gcGFydGljdWxhciBzdGF0aW9ucyBvciBkdXJpbmcgcGFydGljdWxhciBtb250aHM/IFRoaXMgaXMgYSBuYXR1cmFsIGV4dGVuc2lvbiBvZiBvdXIgYW5hbHlzaXMgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIGZ1cnRoZXIgZXhwbG9yZSB0aW1lIHBlcm1pdHRpbmcuIEhvd2V2ZXIsIHdlIGJlbGlldmUgdGhhdCBvdXIgY3VycmVudCBzZXQgb2YgYW5hbHlzaXMgb24gdXNlciBwcm9maWxlcyBwcmVzZW50ZWQgaGVyZSBzZXJ2ZXMgdG8gcHJvdmlkZSBhbiBvdmVyYXJjaGluZyByZXByZXNlbnRhdGlvbiBvZiB1c2VyIHByb2ZpbGVzLgoKIyMgQ29uY2x1c2lvbgoKSW4gdGhpcyBwcm9qZWN0LCB3ZSBleGFtaW5lZCBkYXRhIHJlbGF0ZWQgdG8gUGhpbGFkZWxwaGlhJ3MgYmlrZSBzaGFyZSBwcm9ncmFtLCBJbmRlZ28uIFdlIGV4YW1pbmVkIGFsbCBhdmFpbGFibGUgcXVhcnRlcmx5IGRhdGEgKFEyIG9mIDIwMTUgdG8gUTQgb2YgMjAxNikgdG8gaWRlbnRpZnkgbm90ZXdvcnRoeSBwYXR0ZXJucyBhbmQgdHJlbmRzIHJlbGF0ZWQgdG8gdGhlIHZhcmlvdXMgd2F5cyB0aGF0IEluZGVnbyBpcyB1dGlsaXplZCwgbWFpbmx5IHBlcnRhaW5pbmcgdG8gc3RhdGlvbiB1c2FnZSwgZGFpbHkgdHJhZmZpYywgdHJpcCBkdXJhdGlvbnMsIGFuZCB1c2VyIHByb2ZpbGVzLiBXZSBmb3VuZCB0aGF0IHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMgYXJlIGNvbmNlbnRyYXRlZCBhcm91bmQgbG9jYXRpb25zIHRoYXQgYXJlIGRlbnNlbHkgcG9wdWxhdGVkIChlLmcuIGluIGNsb3NlIHByb3hpbWl0eSB0byBjaXR5IGNlbnRlcnMgb3IgdW5pdmVyc2l0eSBjYW1wdXNlcykuIFdlIGFsc28gbm90ZSB0aGF0IHNlYXNvbmFsaXR5IGFmZmVjdHMgdGhlIHVzYWdlIHJhdGUgb2YgY2VydGFpbiBJbmRlZ28gc3RhdGlvbnMuIFVzaW5nIGRhaWx5IHRyYWZmaWMgYXMgYSBwcm94eSB0byBkZXRlcm1pbmUgd2hldGhlciBvciBub3QgSW5kZWdvIGlzIHVzZWQgYXMgYSBtZWFucyBvZiBjb21tdXRlLCB3ZSBhbHNvIGZvdW5kIGFuIGluY3JlYXNlIGluIGZyZXF1ZW5jeSBvZiBiaWtlIHRyaXBzIG9uIHdlZWtkYXlzIHRoYXQgY29udmVuaWVudGx5IG92ZXJsYXBzIHdpdGggdGltZSBwZXJpb2RzIHdoZW4gbW9zdCBwZW9wbGUgY29tbXV0ZSB0byBhbmQgZnJvbSB3b3JrIG9yIHNjaG9vbC4gTW9yZW92ZXIsIHdlIG9ic2VydmUgYSBoaWdoZXIgdm9sdW1lIG9mIGJpa2UgdHJpcHMgZHVyaW5nIHRoZSB3ZWVrZGF5LCBhbGwgb2Ygd2hpY2ggY29uamVjdHVyYWxseSBkZW1vc3RyYXRlIHRoZSB2ZXJjYWl0eSBvZiBvdXIgYWZvcmVtZW50aW9uZWQgY2xhaW0uIAoKV2UgeWV0IGFnYWluIG9ic2VydmUgdHJlbmRzIG9mIHNlYXNvbmFsaXR5IGluIGRhaWx5IHRyYWZmaWMsIGluIHdoaWNoIHZvbHVtZSBkdXJpbmcgdGhlIHN1bW1lciBmYXIgZXhjZWVkcyB0aGF0IG9mIHdpbnRlciBtb250aHMuIFdlIGFsc28gZXhhbWluZWQgdHJpcCBkdXJhdGlvbnMsIGFuZCBmb3VuZCB0aGF0IHNob3J0ZXIgdHJpcHMgKDAtMTUgbWludXRlcykgYXJlIHRoZSBub3JtLCBidXQgdGhpcyBpcyBsZXNzIGV2aWRlbnQgZm9yIHdlZWtlbmRzIHRoYW4gd2Vla2RheXMuIEZpbmFsbHksIHdlIGV2YWx1YXRlZCB0aGUgcHJvcG9ydGlvbnMgb2YgSW5kZWdvJ3MgdXNlciBwcm9maWxlcyBhbmQgZm91bmQgdGhhdCBtb3N0IHVzZXJzIHByZWZlciBtb250aGx5IHBheW1lbnRzIGFuZCBvbmUtd2F5IHRyaXBzLiBXZSBhbHNvIGZvdW5kIHRoYXQgb3ZlciB0aW1lLCBtb3JlIGluZGl2aWR1YWxzIGFyZSBvcHRpbmcgZm9yIHllYXJseSBtZW1lYmVyc2hpcHMsIHdoaWNoIG1heSBzcGVhayB0byB0aGUgZmFjdCB0aGF0IEluZGVnbyBpcyBiZWNvbWluZyBhIG1vcmUgcG9wdWxhciBtZWFucyBvZiBzaG9ydC1yYW5nZSBjb21tdXRlIGluIFBoaWxhZGVscGhpYS4gCgpJdCBpcyB3b3J0aCBtZW50aW9uaW5nIHRoYXQgb3VyIGFuYWx5c2VzIGlzIGV4dHJlbWVseSBicm9hZCBpbiBuYXR1cmUuIFdlIHdvdWxkIGhhdmUgbGlrZWQgdG8gZnVydGhlciBleHBsb3JlIGNlcnRhaW4gdHJlbmRzIGFuZCBpbnRlcmFjdGlvbiBlZmZlY3RzLiBGb3IgaW5zdGFuY2UsIGhvdyBkb2VzIGNlcnRhaW4gdXNlciBwcm9maWxlcyBhbmQgc3RhdGlvbnMgY29ycmVsYXRlIHdpdGggZWFjaCBvdGhlcj8gQXJlIHRoZXJlIGFueSBuZWlnaGJvcmhvb2RzIHRoYXQgaGF2ZSBleHRyZW1lbHkgaGlnaCB2b2x1bWVzIG9mIHRyYWZmaWM/IEhvdyBkb2VzIHRoaXMgdmFyeSBtb250aGx5LCBxdWFydGVybHksIG9yIGV2ZW4geWVhcmx5PyBXZSB3b3VsZCBoYXZlIGxpa2VkIHRvIGNvbXBhcmVkIHJlc3VsdHMgYWNyb3NzIHNpbWlsYXIgY2l0aWVzLCBhbmQgaG93IHRoZXkgY29tcGFyZSBhbmQgY29uc3RyYXN0IChlLmcuIHdpdGggTllDJ3MgY2l0aWJpa2UpLiBUaW1lIHBlcm1pdHRpbmcsIHRoZXNlIGFyZSB0aGUgbW9yZSBpbW1pbmVudCBhdmVudWVzIHdlIHdvdWxkIGhhdmUgbGlrZWQgdG8gZXhwbG9yZS4gCgpBbm90aGVyIGNhdmVhdCB0aGF0IHNob3VsZCBiZSBicm91Z2h0IHRvIGxpZ2h0IGlzIHRoYXQgdGhlIGxhY2sgb2YgaGlzdG9yaWNhbCBkYXRhIGFsc28gbWFrZXMgaXQgZGlmZmljdWx0IHRvIGRyYXcgcm9idXN0IGNvbmNsdXNpb25zIG9uIHllYXItdG8teWVhciB2YXJpYWJpbGl0eSBvciBldmVuIGNvcnJvYm9yYXRlIHRoZSBzaWduaWZpY2FuY2Ugb2Ygc2Vhc29uYWwgZmx1Y3R1YXRpb25zLiBUaGlzIHByb2JsZW0gb2J2aW91c2x5IGJlY29tZXMgbGVzcyByZWxldmFudCBhcyBtb3JlIHF1YXJ0ZXJseSBkYXRhIGJlY29tZXMgYXZhaWxhYmxlLCBidXQgaXMgYSBtYWpvciBvYnN0YWNsZSBpbiBvdXIgY3VycmVudCBhbmFseXNpcyBmb3IgbG9uZy10ZXJtIHRpbWUgZnJhbWVzLiAKCk5vbmV0aGVsZXNzLCB3aXRoIHRoZSBkYXRhIGF2YWlsYWJsZSwgd2UgYmVsaWV2ZSB0aGF0IHRoZSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9ucyB0aGF0IHdlIHByb3ZpZGUgaGVyZSBzZXJ2ZSBhcyBhIGdvb2QgZmlyc3Qtb3JkZXIgYXBwcm94aW1hdGlvbiBvZiBzYWxpZW50IHRyZW5kcyByZWxhdGVkIHRvIEluZGVnbyBhbmQgdGhlIHdheXMgaXQgaXMgYmVpbmcgZWZmZWN0aXZlbHkgdXRpbGl6ZWQuIFdlIGhvcGUgdGhpcyBzZXJ2ZXMgYXMgYSBwcmFnbWF0aWMgY2F0YWx5c3QgZm9yIG90aGVyIHJlYWRlcnMgaW50ZXJlc3RlZCBpbiB0aGUgSW5kZWdvIGRhdGFzZXQsIG9yIHVzaW5nIHRoZSBpbnNpZ2h0cyBhbmQgdGVjaGlucXVlcyBwcm92aWRlZCBoZXJlIHRvIGV4cGxvcmUgb3RoZXIgc2ltaWxhciBkYXRhc2V0cy4KCiMjIEFwcGVuZGl4OiBTaGlueSBhcHAKCldlIGFsc28gY3JlYXRlZCBhIFtTaGlueSBhcHBsaWNhdGlvbl0oaHR0cHM6Ly9naXRodWIuY29tL21hc29uc3VuL2dyNTcwMi1wcm9qKSBmb3IgcmVhbC10aW1lIHN0YXR1cyB1cGRhdGVzIGZvciBhbGwgYWN0aXZlIEluZGVnbyBzdGF0aW9ucy4gVGhpcyBpcyBwdXJlbHkgc3VwcGxlbWVudGFsLCBhbmQgd2UgcHJvdmlkZSBzbmFwc2hvdHMgaGVyZSwgYW5kIGluc3RydWN0aW9ucyB0byBydW4gb3VyIGFwcGxpY2F0aW9uIGluIG91ciBHaXRodWIgcmVwbyBsaW5rZWQgYWJvdmUuIFlvdSBjYW4gZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMgb24gdGhlIHdlYnNpdGUgdG8gcnVuIGl0IG9uIGEgbG9jYWwgbWFjaGluZS4g